perf: only update draggable x/y state at end of drag (#1379)
* perf: only update draggable x/y state at end of drag This is more intelligent and more performant than using requestIdleCallback willy-nilly. We can just update the store when the user is actually done dragging the button. * remove console.log * consistent syntax
This commit is contained in:
parent
a5f68aa45c
commit
b2d7fad435
|
@ -31,19 +31,17 @@
|
|||
</style>
|
||||
<script>
|
||||
import { observe } from 'svelte-extras'
|
||||
import {
|
||||
throttleRequestAnimationFrame,
|
||||
throttleRequestPostAnimationFrame
|
||||
} from '../_utils/throttleTimers'
|
||||
import { throttleTimer } from '../_utils/throttleTimer'
|
||||
import { pointerUp, pointerDown, pointerLeave, pointerMove } from '../_utils/pointerEvents'
|
||||
import { requestPostAnimationFrame } from '../_utils/requestPostAnimationFrame'
|
||||
|
||||
// ensure DOM writes only happen once after a rAF
|
||||
const updateIndicatorStyle = throttleRequestAnimationFrame()
|
||||
const updateIndicatorClass = throttleRequestAnimationFrame()
|
||||
const updateDraggableClass = throttleRequestAnimationFrame()
|
||||
const updateIndicatorStyle = throttleTimer(requestAnimationFrame)
|
||||
const updateIndicatorClass = throttleTimer(requestAnimationFrame)
|
||||
const updateDraggableClass = throttleTimer(requestAnimationFrame)
|
||||
|
||||
// ensure DOM reads only happen once after a rPAF
|
||||
const calculateGBCR = throttleRequestPostAnimationFrame()
|
||||
const calculateGBCR = throttleTimer(requestPostAnimationFrame)
|
||||
|
||||
const clamp = x => Math.max(0, Math.min(1, x))
|
||||
|
||||
|
@ -53,7 +51,9 @@
|
|||
if (dragging) {
|
||||
this.fire('dragStart')
|
||||
} else {
|
||||
const { x, y } = this.get()
|
||||
this.fire('dragEnd')
|
||||
this.fire('change', { x, y })
|
||||
}
|
||||
}, { init: false })
|
||||
this.observe('indicatorStyle', indicatorStyle => {
|
||||
|
@ -115,7 +115,6 @@
|
|||
const x = clamp((e.clientX - rect.left - offsetX) / rect.width)
|
||||
const y = clamp((e.clientY - rect.top - offsetY) / rect.height)
|
||||
this.set({ x, y })
|
||||
this.fire('change', { x, y })
|
||||
})
|
||||
}
|
||||
},
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
indicatorHeight={52}
|
||||
x={indicatorX}
|
||||
y={indicatorY}
|
||||
on:dragStart="onDragStart()"
|
||||
on:dragEnd="onDragEnd()"
|
||||
on:change="onDraggableChange(event)"
|
||||
on:dragStart="set({dragging: true})"
|
||||
on:dragEnd="set({dragging: false})"
|
||||
>
|
||||
<SvgIcon
|
||||
className="media-focal-point-indicator-svg"
|
||||
|
@ -193,11 +193,6 @@
|
|||
import { intrinsicScale } from '../../../_thirdparty/intrinsic-scale/intrinsicScale'
|
||||
import { resize } from '../../../_utils/events'
|
||||
import Draggable from '../../Draggable.html'
|
||||
import { throttleScheduleIdleTask } from '../../../_utils/throttleTimers'
|
||||
|
||||
// Updating the focal points in the store causes a lot of computations (extra JS work),
|
||||
// so we really don't want to do it for every drag event.
|
||||
const updateFocalPointsInStore = throttleScheduleIdleTask()
|
||||
|
||||
const parseAndValidateFloat = rawText => {
|
||||
let float = parseFloat(rawText)
|
||||
|
@ -307,7 +302,7 @@
|
|||
observeAndSync('rawFocusY', 'focusY')
|
||||
},
|
||||
onDraggableChange ({ x, y }) {
|
||||
updateFocalPointsInStore(() => {
|
||||
scheduleIdleTask(() => {
|
||||
const focusX = parseAndValidateFloat(percentToCoords(x * 100))
|
||||
const focusY = parseAndValidateFloat(percentToCoords(100 - (y * 100)))
|
||||
const { realm, index, media } = this.get()
|
||||
|
@ -319,6 +314,12 @@
|
|||
}
|
||||
})
|
||||
},
|
||||
onDragStart () {
|
||||
this.set({ dragging: true })
|
||||
},
|
||||
onDragEnd () {
|
||||
this.set({ dragging: false })
|
||||
},
|
||||
measure () {
|
||||
requestAnimationFrame(() => {
|
||||
if (!this.refs.container) {
|
||||
|
|
20
src/routes/_utils/throttleTimer.js
Normal file
20
src/routes/_utils/throttleTimer.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Sometimes we want to queue multiple requestAnimationFrames but only run the last one.
|
||||
// It's tedious to do this using cancelAnimationFrame, so this is a utility to throttle
|
||||
// a timer such that it only runs the last callback when it fires.
|
||||
|
||||
export const throttleTimer = timer => {
|
||||
let queuedCallback
|
||||
|
||||
const flush = () => {
|
||||
const callback = queuedCallback
|
||||
queuedCallback = null
|
||||
callback()
|
||||
}
|
||||
|
||||
return callback => {
|
||||
if (!queuedCallback) {
|
||||
timer(flush)
|
||||
}
|
||||
queuedCallback = callback
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Sometimes we want to queue multiple requestAnimationFrames but only run the last one.
|
||||
// It's tedious to do this using cancelAnimationFrame, so this is a utility to throttle
|
||||
// a timer such that it only runs the last callback when it fires.
|
||||
|
||||
import { requestPostAnimationFrame } from './requestPostAnimationFrame'
|
||||
import { scheduleIdleTask } from './scheduleIdleTask'
|
||||
|
||||
const throttle = (timer) => {
|
||||
return () => {
|
||||
let queuedCallback
|
||||
|
||||
return function throttledRaf (callback) {
|
||||
const alreadyQueued = !!queuedCallback
|
||||
queuedCallback = callback
|
||||
if (!alreadyQueued) {
|
||||
timer(() => {
|
||||
const cb = queuedCallback
|
||||
queuedCallback = null
|
||||
cb()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const throttleRequestAnimationFrame = throttle(requestAnimationFrame)
|
||||
export const throttleRequestPostAnimationFrame = throttle(requestPostAnimationFrame)
|
||||
export const throttleScheduleIdleTask = throttle(scheduleIdleTask)
|
Loading…
Reference in a new issue