From d763b69999d390b4b95ad1855ea33787aa0383ec Mon Sep 17 00:00:00 2001 From: Jonas Lochmann Date: Mon, 10 May 2021 02:00:00 +0200 Subject: [PATCH] Add remote zoom support --- presentations/demo/index.html | 4 +- remote/plugin.js | 12 +++- remote/remotezoom.js | 104 ++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 remote/remotezoom.js diff --git a/presentations/demo/index.html b/presentations/demo/index.html index a8e707b..6459eff 100644 --- a/presentations/demo/index.html +++ b/presentations/demo/index.html @@ -451,7 +451,7 @@

THE END

- + @@ -499,7 +499,7 @@

THE END

}, // Learn about plugins: https://revealjs.com/plugins/ - plugins: [ RevealZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight, RevealRemote ] + plugins: [ RevealRemoteZoom, RevealNotes, RevealSearch, RevealMarkdown, RevealHighlight, RevealRemote ] }); diff --git a/remote/plugin.js b/remote/plugin.js index c15449d..17b224d 100644 --- a/remote/plugin.js +++ b/remote/plugin.js @@ -186,6 +186,8 @@ reveal.addEventListener("overviewshown", sendMultiplexState); reveal.addEventListener("paused", sendMultiplexState); reveal.addEventListener("resumed", sendMultiplexState); + reveal.addEventListener("enable-zoom", sendMultiplexState); + reveal.addEventListener("disable-zoom", sendMultiplexState); sendMultiplexState(); } @@ -227,7 +229,11 @@ function sendMultiplexState() { - socket.emit("multiplex", { state: reveal.getState() }); + var state = reveal.getState(); + var zoomPlugin = reveal.getPlugin("remote-zoom"); + var zoom = zoomPlugin ? zoomPlugin.getCurrentZoom() : null; + + socket.emit("multiplex", { state: state, zoom: zoom }); } function msgClientConnected() { @@ -235,7 +241,11 @@ } function msgSync(data) { + var zoomPlugin = reveal.getPlugin("remote-zoom"); + reveal.setState(data.state); + + if (zoomPlugin) { zoomPlugin.setCurrentZoom(data.zoom); } } function on(cmd, fn) { diff --git a/remote/remotezoom.js b/remote/remotezoom.js new file mode 100644 index 0000000..e2a7024 --- /dev/null +++ b/remote/remotezoom.js @@ -0,0 +1,104 @@ +const RevealRemoteZoom = () => { + let reveal = null + let currentZoom = null + let currentSlideElement = null + + function allowControl() { + const config = reveal.getConfig() + + return config.controls && config.touch + } + + function dispatchEnableZoom(focus) { + if (!isValidFocus(focus)) { + console.warn('invalid focus parameter for dispatchEnableZoom()'); return + } + + reveal.dispatchEvent({ + type: 'enable-zoom', + data: { focus } + }) + } + + function dispatchDisableZoom() { + reveal.dispatchEvent({ type: 'disable-zoom' }) + } + + const doubleClickListener = (e) => { + if (!allowControl()) return + + if (currentZoom !== null) { + dispatchDisableZoom() + } else { + const location = getRelativeLocation({ element: currentSlideElement, event: e }) + + dispatchEnableZoom(location) + } + } + + function setupForSlideElement(element) { + if (currentSlideElement !== null) { + applyZoom(null) + currentSlideElement.removeEventListener('dblclick', doubleClickListener) + } + + element.addEventListener('dblclick', doubleClickListener) + + currentSlideElement = element + } + + function getRelativeLocation({event, element}) { + const elementLocation = element.getBoundingClientRect() + + const x = Math.round((event.clientX - elementLocation.left) * 100 / (elementLocation.right - elementLocation.left)) + const y = Math.round((event.clientY - elementLocation.top) * 100 / (elementLocation.bottom - elementLocation.top)) + + return { x, y } + } + + function applyZoom(focus) { + if (focus === null) { + currentSlideElement.style.transform = '' + currentZoom = null + } else { + if (!isValidFocus(focus)) { + console.warn('invalid focus parameter for applyZoom()'); return + } + + const transform = 'scale(200%) translate(' + (50 - focus.x) + '%, ' + (50 - focus.y) + '%)' + + currentSlideElement.style.transform = transform + currentZoom = focus + } + } + + function isValidFocus(focus) { + return typeof focus === 'object' && Number.isInteger(focus.x) && Number.isInteger(focus.y) && + focus.x >= 0 && focus.x <= 100 && focus.y >= 0 && focus.y <= 100 && Object.keys(focus).length === 2 + } + + return { + id: 'remote-zoom', + init: (initReveal) => { + reveal = initReveal + + reveal.addEventListener('slidechanged', (e) => setupForSlideElement(e.currentSlide)) + reveal.addEventListener('ready', (e) => setupForSlideElement(e.currentSlide)) + reveal.on('enable-zoom', (e) => applyZoom(e.focus)) + reveal.on('disable-zoom', (e) => applyZoom(null)) + reveal.on('overviewshown', () => dispatchDisableZoom()); + }, + getCurrentZoom: () => currentZoom, + setCurrentZoom: (focus) => { + if (focus === null) { + dispatchDisableZoom() + } else { + if (!isValidFocus(focus)) { + console.warn('invalid focus parameter for setCurrentZoom()'); return + } + + dispatchEnableZoom(focus) + } + } + } +};