-
-
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> |
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
@trafium - How about just passing in the objects and using the properties that are needed inside the functions?
Scott