Skip to content

Instantly share code, notes, and snippets.

@smolinari
Last active September 4, 2019 07:40
Show Gist options
  • Save smolinari/37367d96e800c940c289e86f752c2562 to your computer and use it in GitHub Desktop.
Save smolinari/37367d96e800c940c289e86f752c2562 to your computer and use it in GitHub Desktop.
<template lang="pug">
div.component(ref="el" :style="{ filter: `saturate(${saturation}) brightness(${brightness})` }")
div
div Brightness: {{brightness}}
div Saturation: {{saturation}}
div Width: {{width}}
div Height: {{height}}
div X: {{x}}
div Y: {{y}}
</template>
<script>
import { onBeforeUnmount, onMounted, reactive, ref, toRefs, watch } from '@vue/composition-api'
export default {
setup () {
let el = ref(null)
const { x, y } = useRelativeMousePosition(el)
const { width, height } = useSizeObserver(el)
const { brightness } = useBrightness(x, width)
const { saturation } = useSaturation(y, height)
return {
el,
width,
height,
x,
y,
brightness,
saturation
}
}
}
function useRelativeMousePosition (el) {
let mousePosition = reactive({
x: 0,
y: 0
})
function update (event) {
const rect = el.getBoundingClientRect()
mousePosition.x = event.clientX - rect.left
mousePosition.y = event.clientY - rect.top
}
onMounted(() => window.addEventListener('mousemove', update))
onBeforeUnmount(() => window.removeEventListener('mousemove', update))
return toRefs(mousePosition)
function useSizeObserver (el) {
let attrs = reactive({
width: 0,
height: 0
})
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
attrs.width = entry.contentRect.width
attrs.height = entry.contentRect.height
}
})
onMounted(() => resizeObserver.observe(el))
onBeforeUnmount(() => resizeObserver.unobserve(el))
return toRefs(attrs)
}
function useBrightness (value, maxValue) {
let brightnessRef = ref(0)
watch([value, maxValue], ([value, max]) => {
brightnessRef = cappedValue(value, { max, min: 0 })
}, { lazy: true })
return { brightnessRef }
}
function useSaturation (value, maxValue) {
let saturationRef = ref(0)
watch([value, maxValue], ([value, max]) => {
saturationRef = cappedValue(value, { max, min: 0 })
}, { lazy: true })
return { saturationRef }
}
function cappedValue (value, { max, min }) {
return Math.max(min, Math.min(value, max)) / max
}
</script>
<style
lang="scss"
scoped
>
.component {
max-width: 700px;
min-height: 300px;
background: royalblue;
color: white;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
</style>
@trafium
Copy link

trafium commented Sep 2, 2019

Sadly it cannot work like this, particularly lines #20 and #21. As soon as you do someReactiveObject.someAttribute - returned value is just a primitive passed by value and not reactive, and watch can work only with reactive objects or refs (https://vue-composition-api-rfc.netlify.com/api.html#watch).

@voluntadpear
Copy link

An ESLint rule to detect attempts of destructuring of reactives or mutating a ref without using .value would be needed once Vue 3.0 is released

@smolinari
Copy link
Author

smolinari commented Sep 2, 2019

@trafium - How about just passing in the objects and using the properties that are needed inside the functions?

Scott

@trafium
Copy link

trafium commented Sep 2, 2019

It seems to me that this pattern would tightly couple functions together, since now useBrightness and useSaturation would require some seemingly unnecessary argument structure in order to behave correctly.

@smolinari
Copy link
Author

Yeah. I agree. It's not pretty. Seems like passing reactivity was easier with the this reference in the options API and we are losing that simplicity.

Scott

@smolinari
Copy link
Author

@trafium - Would this version work?

Scott

@smolinari
Copy link
Author

smolinari commented Sep 2, 2019

Not sure I like the "Ref" postfix. Seems unnecessary now in the setup() function. The "use" prefix on the functions should identify anything returned is a Ref. Right?

Scott

@smolinari
Copy link
Author

I'm taking the "Ref" postfix out.

Scott

@smolinari
Copy link
Author

Not too much different than what you had with just Refs. 😄

Scott

@voluntadpear
Copy link

Refs look more useful and less confusing than reactive and this final version doesn't look much different than the refs version. I don't see when you would prefer reactive over refs.

@doncatnip
Copy link

doncatnip commented Sep 2, 2019

I use it to group properties that really do belong together. A mouse position always has an x and y. A date range that has start, end. In the rare occasion you really only need/can pass the y axis along, i'd destructure it into refs.

I actually use it rather often. It might seem that reactive is superfluous, but at this point I would not want to miss it.

@trafium
Copy link

trafium commented Sep 3, 2019

@smolinari
Will check your solution soon.

I realised that in my use cases i never had to deal with actual mutable lists or other complex data. Took me long to realise, but seems like I can't really do those with refs only without much pain 😅

@trafium
Copy link

trafium commented Sep 3, 2019

@smolinari
Besides couple of typos there are still problems with lines #20 and #21 because primitive values are passed into functions instead of refs/reactive data.

@smolinari
Copy link
Author

@trafium - And if just the variable is passed in without value? Using value in the function?

Scott

@trafium
Copy link

trafium commented Sep 3, 2019

@smolinari
Yes, in fact we don't need to use .value at all since we would pass ref into watch and receive plain values in callback function ([value, max] part)

@smolinari
Copy link
Author

@trafium - edited. Will that work?

Scott

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment