|
191 | 191 |
|
192 | 192 | // GLOBALS AND DEFAULTS |
193 | 193 |
|
194 | | - // Getting cross-browser transitionEnd event name. |
195 | | - // It's hard to detect it, so we are using the list based on |
196 | | - // https://developer.mozilla.org/en/CSS/CSS_transitions |
197 | | - var transitionEnd = ({ |
198 | | - 'transition' : 'transitionEnd', |
199 | | - 'OTransition' : 'oTransitionEnd', |
200 | | - 'msTransition' : 'MSTransitionEnd', // who knows how it will end up? |
201 | | - 'MozTransition' : 'transitionend', |
202 | | - 'WebkitTransition' : 'webkitTransitionEnd' |
203 | | - })[pfx("transition")]; |
204 | | - |
205 | 194 | // This is were the root elements of all impress.js instances will be kept. |
206 | 195 | // Yes, this means you can have more than one instance on a page, but I'm not |
207 | 196 | // sure if it makes any sense in practice ;) |
|
304 | 293 | } |
305 | 294 | }; |
306 | 295 |
|
307 | | - // To detect the moment when the transition to step element finished |
308 | | - // we need to handle the transitionEnd event. |
309 | | - // |
310 | | - // It may not sound very hard but to makes things a little bit more |
311 | | - // complicated there are two elements being animated separately: |
312 | | - // `root` (used for scaling) and `canvas` for translate and rotations. |
313 | | - // Transitions on them are triggered with different delays (to make |
314 | | - // visually nice and 'natural' looking transitions), so we need to know |
315 | | - // that both of them are finished. |
316 | | - // |
317 | | - // It sounds like a simple counter to two would be enough. Unfortunately |
318 | | - // if there is no change in the transform value (for example scale doesn't |
319 | | - // change between two steps) only one transition (and transitionEnd event) |
320 | | - // will be triggered. |
321 | | - // |
322 | | - // So to properly detect when the transitions finished we need to keep |
323 | | - // the `expectedTransitionTarget` (that can be one of `root` or `canvas`) |
324 | | - // and only call `onStepEnter` then transition ended on the expected one. |
325 | | - |
326 | | - var expectedTransitionTarget = null; |
327 | | - |
328 | | - var onTransitionEnd = function (event) { |
329 | | - if (event.target === expectedTransitionTarget) { |
330 | | - onStepEnter(activeStep); |
331 | | - } |
332 | | - }; |
333 | | - |
334 | 296 | // `initStep` initializes given step element by reading data from its |
335 | 297 | // data attributes and setting correct styles. |
336 | 298 | var initStep = function ( el, idx ) { |
|
421 | 383 | }); |
422 | 384 | css(canvas, rootStyles); |
423 | 385 |
|
424 | | - root.addEventListener(transitionEnd, onTransitionEnd, false); |
425 | | - |
426 | 386 | body.classList.remove("impress-disabled"); |
427 | 387 | body.classList.add("impress-enabled"); |
428 | 388 |
|
|
455 | 415 | return (step && step.id && stepsData["impress-" + step.id]) ? step : null; |
456 | 416 | }; |
457 | 417 |
|
| 418 | + // used to reset timeout for `impress:stepenter` event |
| 419 | + var stepEnterTimeout = null; |
| 420 | + |
458 | 421 | // `goto` API function that moves to step given with `el` parameter (by index, id or element), |
459 | 422 | // with a transition `duration` optionally given as second parameter. |
460 | 423 | var goto = function ( el, duration ) { |
|
518 | 481 |
|
519 | 482 | var targetScale = target.scale * windowScale; |
520 | 483 |
|
521 | | - // Because one of the transition is delayed depending on zoom direction, |
522 | | - // the last transition will happen on `root` or `canvas` element. |
523 | | - // Here we store the expected transition event target, to be able to correctly |
524 | | - // trigger `impress:stepenter` event. |
525 | | - expectedTransitionTarget = target.scale > currentState.scale ? root : canvas; |
526 | | - |
527 | 484 | // trigger leave of currently active element (if it's not the same step again) |
528 | 485 | if (activeStep && activeStep !== el) { |
529 | 486 | onStepLeave(activeStep); |
530 | 487 | } |
531 | 488 |
|
532 | | - // alter transforms of `root` and `canvas` to trigger transitions |
| 489 | + // Now we alter transforms of `root` and `canvas` to trigger transitions. |
| 490 | + // |
| 491 | + // And here is why there are two elements: `root` and `canvas` - they are |
| 492 | + // being animated separately: |
| 493 | + // `root` is used for scaling and `canvas` for translate and rotations. |
| 494 | + // Transitions on them are triggered with different delays (to make |
| 495 | + // visually nice and 'natural' looking transitions), so we need to know |
| 496 | + // that both of them are finished. |
533 | 497 | css(root, { |
534 | 498 | // to keep the perspective look similar for different scales |
535 | 499 | // we need to 'scale' the perspective, too |
|
544 | 508 | transitionDelay: (zoomin ? 0 : delay) + "ms" |
545 | 509 | }); |
546 | 510 |
|
| 511 | + // Here is a tricky part... |
| 512 | + // |
| 513 | + // If there is no change in scale or no change in rotation and translation, it means there was actually |
| 514 | + // no delay - because there was no transition on `root` or `canvas` elements. |
| 515 | + // We want to trigger `impress:stepenter` event in the correct moment, so here we compare the current |
| 516 | + // and target values to check if delay should be taken into account. |
| 517 | + // |
| 518 | + // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on |
| 519 | + // - it's simply comparing all the values. |
| 520 | + if ( currentState.scale === target.scale || |
| 521 | + (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y && |
| 522 | + currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x && |
| 523 | + currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) { |
| 524 | + delay = 0; |
| 525 | + } |
| 526 | + |
547 | 527 | // store current state |
548 | 528 | currentState = target; |
549 | 529 | activeStep = el; |
550 | 530 |
|
551 | | - // manually trigger enter event if duration was set to 0 |
552 | | - if (duration === 0) { |
| 531 | + // And here is where we trigger `impress:stepenter` event. |
| 532 | + // We simply set up a timeout to fire it taking transition duration (and possible delay) into account. |
| 533 | + // |
| 534 | + // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way |
| 535 | + // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend` |
| 536 | + // event is only triggered when there was a transition (change in the values) caused some bugs and |
| 537 | + // made the code really complicated, cause I had to handle all the conditions separately. And it still |
| 538 | + // needed a `setTimeout` fallback for the situations when there is no transition at all. |
| 539 | + // So I decided that I'd rather make the code simpler than use shiny new `transitionend`. |
| 540 | + // |
| 541 | + // If you want learn something interesting and see how it was done with `transitionend` go back to |
| 542 | + // verison 0.5.2 of impress.js on GitHub. |
| 543 | + window.clearTimeout(stepEnterTimeout); |
| 544 | + stepEnterTimeout = window.setTimeout(function() { |
553 | 545 | onStepEnter(activeStep); |
554 | | - } |
| 546 | + }, duration + delay); |
555 | 547 |
|
556 | 548 | return el; |
557 | 549 | }; |
|
0 commit comments