blob: 960fb8108907d55523f916b0a0b747b7eac3080e [file] [log] [blame]
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02001/*!
2 * reveal.js Zoom plugin
3 */
4const Plugin = {
JJ Allaireefa6ad42016-01-30 13:12:05 -05005
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02006 id: 'zoom',
JJ Allaireefa6ad42016-01-30 13:12:05 -05007
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +02008 init: function( reveal ) {
JJ Allaireefa6ad42016-01-30 13:12:05 -05009
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020010 reveal.getRevealElement().addEventListener( 'mousedown', function( event ) {
11 var defaultModifier = /Linux/.test( window.navigator.platform ) ? 'ctrl' : 'alt';
JJ Allaireefa6ad42016-01-30 13:12:05 -050012
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020013 var modifier = ( reveal.getConfig().zoomKey ? reveal.getConfig().zoomKey : defaultModifier ) + 'Key';
14 var zoomLevel = ( reveal.getConfig().zoomLevel ? reveal.getConfig().zoomLevel : 2 );
JJ Allaireefa6ad42016-01-30 13:12:05 -050015
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020016 if( event[ modifier ] && !reveal.isOverview() ) {
17 event.preventDefault();
JJ Allaireefa6ad42016-01-30 13:12:05 -050018
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020019 zoom.to({
20 x: event.clientX,
21 y: event.clientY,
22 scale: zoomLevel,
23 pan: false
24 });
25 }
26 } );
27
Marc Kupietz09b75752023-10-07 09:32:19 +020028 },
29
30 destroy: () => {
31
32 zoom.reset();
33
Christophe Dervieuxe1893ae2021-10-07 17:09:02 +020034 }
35
36};
37
38export default () => Plugin;
JJ Allaireefa6ad42016-01-30 13:12:05 -050039
40/*!
41 * zoom.js 0.3 (modified for use with reveal.js)
42 * http://lab.hakim.se/zoom-js
43 * MIT licensed
44 *
45 * Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
46 */
47var zoom = (function(){
48
49 // The current zoom level (scale)
50 var level = 1;
51
52 // The current mouse position, used for panning
53 var mouseX = 0,
54 mouseY = 0;
55
56 // Timeout before pan is activated
57 var panEngageTimeout = -1,
58 panUpdateInterval = -1;
59
60 // Check for transform support so that we can fallback otherwise
Marc Kupietz09b75752023-10-07 09:32:19 +020061 var supportsTransforms = 'transform' in document.body.style;
JJ Allaireefa6ad42016-01-30 13:12:05 -050062
63 if( supportsTransforms ) {
64 // The easing that will be applied when we zoom in/out
65 document.body.style.transition = 'transform 0.8s ease';
JJ Allaireefa6ad42016-01-30 13:12:05 -050066 }
67
68 // Zoom out if the user hits escape
69 document.addEventListener( 'keyup', function( event ) {
70 if( level !== 1 && event.keyCode === 27 ) {
71 zoom.out();
72 }
73 } );
74
75 // Monitor mouse movement for panning
76 document.addEventListener( 'mousemove', function( event ) {
77 if( level !== 1 ) {
78 mouseX = event.clientX;
79 mouseY = event.clientY;
80 }
81 } );
82
83 /**
84 * Applies the CSS required to zoom in, prefers the use of CSS3
85 * transforms but falls back on zoom for IE.
86 *
87 * @param {Object} rect
88 * @param {Number} scale
89 */
90 function magnify( rect, scale ) {
91
92 var scrollOffset = getScrollOffset();
93
94 // Ensure a width/height is set
95 rect.width = rect.width || 1;
96 rect.height = rect.height || 1;
97
98 // Center the rect within the zoomed viewport
99 rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2;
100 rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2;
101
102 if( supportsTransforms ) {
103 // Reset
104 if( scale === 1 ) {
105 document.body.style.transform = '';
JJ Allaireefa6ad42016-01-30 13:12:05 -0500106 }
107 // Scale
108 else {
109 var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
110 transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')';
111
112 document.body.style.transformOrigin = origin;
JJ Allaireefa6ad42016-01-30 13:12:05 -0500113 document.body.style.transform = transform;
JJ Allaireefa6ad42016-01-30 13:12:05 -0500114 }
115 }
116 else {
117 // Reset
118 if( scale === 1 ) {
119 document.body.style.position = '';
120 document.body.style.left = '';
121 document.body.style.top = '';
122 document.body.style.width = '';
123 document.body.style.height = '';
124 document.body.style.zoom = '';
125 }
126 // Scale
127 else {
128 document.body.style.position = 'relative';
129 document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px';
130 document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px';
131 document.body.style.width = ( scale * 100 ) + '%';
132 document.body.style.height = ( scale * 100 ) + '%';
133 document.body.style.zoom = scale;
134 }
135 }
136
137 level = scale;
138
139 if( document.documentElement.classList ) {
140 if( level !== 1 ) {
141 document.documentElement.classList.add( 'zoomed' );
142 }
143 else {
144 document.documentElement.classList.remove( 'zoomed' );
145 }
146 }
147 }
148
149 /**
150 * Pan the document when the mosue cursor approaches the edges
151 * of the window.
152 */
153 function pan() {
154 var range = 0.12,
155 rangeX = window.innerWidth * range,
156 rangeY = window.innerHeight * range,
157 scrollOffset = getScrollOffset();
158
159 // Up
160 if( mouseY < rangeY ) {
161 window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
162 }
163 // Down
164 else if( mouseY > window.innerHeight - rangeY ) {
165 window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
166 }
167
168 // Left
169 if( mouseX < rangeX ) {
170 window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
171 }
172 // Right
173 else if( mouseX > window.innerWidth - rangeX ) {
174 window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
175 }
176 }
177
178 function getScrollOffset() {
179 return {
180 x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
181 y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset
182 }
183 }
184
185 return {
186 /**
187 * Zooms in on either a rectangle or HTML element.
188 *
189 * @param {Object} options
190 * - element: HTML element to zoom in on
191 * OR
192 * - x/y: coordinates in non-transformed space to zoom in on
193 * - width/height: the portion of the screen to zoom in on
194 * - scale: can be used instead of width/height to explicitly set scale
195 */
196 to: function( options ) {
197
198 // Due to an implementation limitation we can't zoom in
199 // to another element without zooming out first
200 if( level !== 1 ) {
201 zoom.out();
202 }
203 else {
204 options.x = options.x || 0;
205 options.y = options.y || 0;
206
207 // If an element is set, that takes precedence
208 if( !!options.element ) {
209 // Space around the zoomed in element to leave on screen
210 var padding = 20;
211 var bounds = options.element.getBoundingClientRect();
212
213 options.x = bounds.left - padding;
214 options.y = bounds.top - padding;
215 options.width = bounds.width + ( padding * 2 );
216 options.height = bounds.height + ( padding * 2 );
217 }
218
219 // If width/height values are set, calculate scale from those values
220 if( options.width !== undefined && options.height !== undefined ) {
221 options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
222 }
223
224 if( options.scale > 1 ) {
225 options.x *= options.scale;
226 options.y *= options.scale;
227
228 magnify( options, options.scale );
229
230 if( options.pan !== false ) {
231
232 // Wait with engaging panning as it may conflict with the
233 // zoom transition
234 panEngageTimeout = setTimeout( function() {
235 panUpdateInterval = setInterval( pan, 1000 / 60 );
236 }, 800 );
237
238 }
239 }
240 }
241 },
242
243 /**
244 * Resets the document zoom state to its default.
245 */
246 out: function() {
247 clearTimeout( panEngageTimeout );
248 clearInterval( panUpdateInterval );
249
250 magnify( { x: 0, y: 0 }, 1 );
251
252 level = 1;
253 },
254
255 // Alias
256 magnify: function( options ) { this.to( options ) },
257 reset: function() { this.out() },
258
259 zoomLevel: function() {
260 return level;
261 }
262 }
263
264})();