@@ -30,19 +30,60 @@
}
diff --git a/src/routes/_components/dialog/components/MediaFocalPointDialog.html b/src/routes/_components/dialog/components/MediaFocalPointDialog.html
index 6db356fd..83b66437 100644
--- a/src/routes/_components/dialog/components/MediaFocalPointDialog.html
+++ b/src/routes/_components/dialog/components/MediaFocalPointDialog.html
@@ -25,12 +25,14 @@
{
let float = parseFloat(rawText)
@@ -208,6 +222,7 @@
Draggable
},
data: () => ({
+ dragging: false,
rawFocusX: '0',
rawFocusY: '0',
containerWidth: 0,
@@ -276,8 +291,6 @@
})
},
setupSyncToStore () {
- const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
-
const observeAndSync = (rawKey, key) => {
this.observe(rawKey, rawFocus => {
const { realm, index, media } = this.get()
@@ -285,7 +298,7 @@
if (media[index][key] !== rawFocusDecimal) {
media[index][key] = rawFocusDecimal
this.store.setComposeData(realm, { media })
- saveStore()
+ scheduleIdleTask(() => this.store.save())
}
}, { init: false })
}
@@ -294,16 +307,16 @@
observeAndSync('rawFocusY', 'focusY')
},
onDraggableChange ({ x, y }) {
- const saveStore = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
-
- scheduleIdleTask(() => {
- const focusX = percentToCoords(x * 100)
- const focusY = percentToCoords(100 - (y * 100))
+ updateFocalPointsInStore(() => {
+ const focusX = parseAndValidateFloat(percentToCoords(x * 100))
+ const focusY = parseAndValidateFloat(percentToCoords(100 - (y * 100)))
const { realm, index, media } = this.get()
- media[index].focusX = parseAndValidateFloat(focusX)
- media[index].focusY = parseAndValidateFloat(focusY)
- this.store.setComposeData(realm, { media })
- saveStore()
+ if (media[index].focusX !== focusX || media[index].focusY !== focusY) {
+ media[index].focusX = focusX
+ media[index].focusY = focusY
+ this.store.setComposeData(realm, { media })
+ scheduleIdleTask(() => this.store.save())
+ }
})
},
measure () {
diff --git a/src/routes/_utils/asyncPolyfills.js b/src/routes/_utils/asyncPolyfills.js
index 7b2778b4..8fd253ae 100644
--- a/src/routes/_utils/asyncPolyfills.js
+++ b/src/routes/_utils/asyncPolyfills.js
@@ -13,7 +13,3 @@ export const importIndexedDBGetAllShim = () => import(
export const importCustomElementsPolyfill = () => import(
/* webpackChunkName: '$polyfill$-@webcomponents/custom-elements' */ '@webcomponents/custom-elements'
)
-
-export const importPointerEventsPolyfill = () => import(
- /* webpackChunkName: '$polyfill$-@wessberg/pointer-events' */ '@wessberg/pointer-events'
- )
diff --git a/src/routes/_utils/loadPolyfills.js b/src/routes/_utils/loadPolyfills.js
index 5a8eb102..8c7fea51 100644
--- a/src/routes/_utils/loadPolyfills.js
+++ b/src/routes/_utils/loadPolyfills.js
@@ -2,8 +2,7 @@ import {
importCustomElementsPolyfill,
importIndexedDBGetAllShim,
importIntersectionObserver,
- importRequestIdleCallback,
- importPointerEventsPolyfill
+ importRequestIdleCallback
} from './asyncPolyfills'
export function loadPolyfills () {
@@ -11,7 +10,6 @@ export function loadPolyfills () {
typeof IntersectionObserver === 'undefined' && importIntersectionObserver(),
typeof requestIdleCallback === 'undefined' && importRequestIdleCallback(),
!IDBObjectStore.prototype.getAll && importIndexedDBGetAllShim(),
- typeof customElements === 'undefined' && importCustomElementsPolyfill(),
- typeof PointerEvent === 'undefined' && importPointerEventsPolyfill()
+ typeof customElements === 'undefined' && importCustomElementsPolyfill()
])
}
diff --git a/src/routes/_utils/pointerEvents.js b/src/routes/_utils/pointerEvents.js
new file mode 100644
index 00000000..051bcfb8
--- /dev/null
+++ b/src/routes/_utils/pointerEvents.js
@@ -0,0 +1,53 @@
+import { get } from './lodash-lite'
+
+const hasPointerEvents = process.browser && typeof PointerEvent === 'function'
+
+// Epiphany browser reports that it's a touch device even though it's not
+const isTouchDevice = process.browser && 'ontouchstart' in document && !/Epiphany/.test(navigator.userAgent)
+
+let pointerDown
+let pointerUp
+let pointerLeave
+let pointerMove
+
+function createEventListener (event) {
+ return (node, callback) => {
+ const listener = e => {
+ // lightweight polyfill for clientX/clientY in pointer events,
+ // which is slightly different in touch events
+ if (typeof e.clientX !== 'number') {
+ e.clientX = get(e, ['touches', 0, 'clientX'])
+ }
+ if (typeof e.clientY !== 'number') {
+ e.clientY = get(e, ['touches', 0, 'clientY'])
+ }
+ callback(e)
+ }
+
+ node.addEventListener(event, listener)
+ return {
+ destroy () {
+ node.removeEventListener(event, listener)
+ }
+ }
+ }
+}
+
+if (hasPointerEvents) {
+ pointerDown = createEventListener('pointerdown')
+ pointerUp = createEventListener('pointerup')
+ pointerLeave = createEventListener('pointerleave')
+ pointerMove = createEventListener('pointermove')
+} else if (isTouchDevice) {
+ pointerDown = createEventListener('touchstart')
+ pointerUp = createEventListener('touchend')
+ pointerLeave = createEventListener('touchend')
+ pointerMove = createEventListener('touchmove')
+} else {
+ pointerDown = createEventListener('mousedown')
+ pointerUp = createEventListener('mouseup')
+ pointerLeave = createEventListener('mouseleave')
+ pointerMove = createEventListener('mousemove')
+}
+
+export { pointerDown, pointerUp, pointerLeave, pointerMove }
diff --git a/src/routes/_utils/requestPostAnimationFrame.js b/src/routes/_utils/requestPostAnimationFrame.js
new file mode 100644
index 00000000..d218d27f
--- /dev/null
+++ b/src/routes/_utils/requestPostAnimationFrame.js
@@ -0,0 +1,9 @@
+// modeled after https://github.com/andrewiggins/afterframe
+// see also https://github.com/WICG/requestPostAnimationFrame
+export const requestPostAnimationFrame = cb => {
+ requestAnimationFrame(() => {
+ const channel = new MessageChannel()
+ channel.port1.onmessage = cb
+ channel.port2.postMessage(undefined)
+ })
+}
diff --git a/src/routes/_utils/throttleRaf.js b/src/routes/_utils/throttleRaf.js
deleted file mode 100644
index 8755272d..00000000
--- a/src/routes/_utils/throttleRaf.js
+++ /dev/null
@@ -1,18 +0,0 @@
-// ensure callback is only executed once per raf
-export const throttleRaf = () => {
- let rafCallback
- let rafQueued
-
- return function throttledRaf (callback) {
- rafCallback = callback
- if (!rafQueued) {
- rafQueued = true
- requestAnimationFrame(() => {
- const cb = rafCallback
- rafCallback = null
- rafQueued = false
- cb()
- })
- }
- }
-}
diff --git a/src/routes/_utils/throttleTimers.js b/src/routes/_utils/throttleTimers.js
new file mode 100644
index 00000000..bf0aff43
--- /dev/null
+++ b/src/routes/_utils/throttleTimers.js
@@ -0,0 +1,28 @@
+// 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)
diff --git a/src/scss/themes/_base.scss b/src/scss/themes/_base.scss
index 1317d126..84b88a9a 100644
--- a/src/scss/themes/_base.scss
+++ b/src/scss/themes/_base.scss
@@ -124,5 +124,7 @@
--focal-img-backdrop-filter: #{rgba($main-bg-color, 0.5)};
--focal-img-bg: #{rgba($main-bg-color, 0.3)};
--focal-bg: #{rgba($toast-bg, 0.8)};
+ --focal-bg-drag: #{rgba($toast-bg, 0.9)};
+ --focal-bg-hover: #{lighten(rgba($toast-bg, 0.8), 5%)};
--focal-color: #{$secondary-text-color};
}