-
-
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> |
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
@trafium - How about just passing in the objects and using the properties that are needed inside the functions?
Scott
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.
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
@trafium - Would this version work?
Scott
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
I'm taking the "Ref" postfix out.
Scott
Not too much different than what you had with just Refs. 😄
Scott
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.
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.
@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 😅
@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.
@trafium - And if just the variable is passed in without value? Using value in the function?
Scott
@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)
@trafium - edited. Will that work?
Scott
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, andwatch
can work only with reactive objects or refs (https://vue-composition-api-rfc.netlify.com/api.html#watch).