mirror of
https://github.com/blasten/turn.js.git
synced 2026-07-14 13:51:14 +08:00
Replace setInterval with requestAnimationFrame
This commit is contained in:
parent
045845c851
commit
7e7bd0b70a
@ -25,7 +25,8 @@ La compatibilité avec tous les navigateurs et appareils modernes n'est pas enco
|
|||||||
- restaure les pages originales comme enfants directs ;
|
- restaure les pages originales comme enfants directs ;
|
||||||
- restaure les attributs `style` et `class` d'origine ;
|
- restaure les attributs `style` et `class` d'origine ;
|
||||||
- supprime les données jQuery internes de turn.js sans effacer les données utilisateur sans lien.
|
- supprime les données jQuery internes de turn.js sans effacer les données utilisateur sans lien.
|
||||||
- Correction de `animatef(false)` pour que l'annulation d'une animation nettoie bien l'intervalle actif.
|
- Remplacement de la boucle d'animation interne `setInterval` par `requestAnimationFrame`.
|
||||||
|
- Correction de `animatef(false)` pour que l'annulation d'une animation annule bien la frame planifiée.
|
||||||
- Ajout d'une démo navigateur minimale avec jQuery `4.0.0`.
|
- Ajout d'une démo navigateur minimale avec jQuery `4.0.0`.
|
||||||
- Ajout de texte et d'images locales dans la démo minimale.
|
- Ajout de texte et d'images locales dans la démo minimale.
|
||||||
- Correction de la mise en page de la démo minimale afin que les dimensions mesurées par turn.js correspondent à la taille réellement rendue des pages.
|
- Correction de la mise en page de la démo minimale afin que les dimensions mesurées par turn.js correspondent à la taille réellement rendue des pages.
|
||||||
@ -167,6 +168,7 @@ La suite actuelle couvre :
|
|||||||
- `addPage` / `removePage` dynamiques ;
|
- `addPage` / `removePage` dynamiques ;
|
||||||
- l'enregistrement des namespaces d'événements ;
|
- l'enregistrement des namespaces d'événements ;
|
||||||
- le nettoyage `destroy` et la réinitialisation ;
|
- le nettoyage `destroy` et la réinitialisation ;
|
||||||
|
- la planification et l'annulation `requestAnimationFrame` dans `animatef` ;
|
||||||
- le rendu de la démo minimale ;
|
- le rendu de la démo minimale ;
|
||||||
- le rendu après page tournée ;
|
- le rendu après page tournée ;
|
||||||
- la géométrie en viewport compact ;
|
- la géométrie en viewport compact ;
|
||||||
|
|||||||
@ -1,154 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
|
|
||||||
<script type="text/javascript" src="../../turn.min.js"></script>
|
|
||||||
|
|
||||||
<style type="text/css">
|
|
||||||
body{
|
|
||||||
background:#ccc;
|
|
||||||
}
|
|
||||||
#book{
|
|
||||||
width:800px;
|
|
||||||
height:500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .turn-page{
|
|
||||||
background-color:white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .cover{
|
|
||||||
background:#333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .cover h1{
|
|
||||||
color:white;
|
|
||||||
text-align:center;
|
|
||||||
font-size:50px;
|
|
||||||
line-height:500px;
|
|
||||||
margin:0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .loader{
|
|
||||||
background-image:url(loader.gif);
|
|
||||||
width:24px;
|
|
||||||
height:24px;
|
|
||||||
display:block;
|
|
||||||
position:absolute;
|
|
||||||
top:238px;
|
|
||||||
left:188px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .data{
|
|
||||||
text-align:center;
|
|
||||||
font-size:40px;
|
|
||||||
color:#999;
|
|
||||||
line-height:500px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls{
|
|
||||||
width:800px;
|
|
||||||
text-align:center;
|
|
||||||
margin:20px 0px;
|
|
||||||
font:30px arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#controls input, #controls label{
|
|
||||||
font:30px arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .odd{
|
|
||||||
background-image:-webkit-linear-gradient(left, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-moz-linear-gradient(left, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-o-linear-gradient(left, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-ms-linear-gradient(left, #FFF 95%, #ddd 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
#book .even{
|
|
||||||
background-image:-webkit-linear-gradient(right, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-moz-linear-gradient(right, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-o-linear-gradient(right, #FFF 95%, #ddd 100%);
|
|
||||||
background-image:-ms-linear-gradient(right, #FFF 95%, #ddd 100%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="book">
|
|
||||||
<div class="cover"><h1>The Bible</h1></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="controls">
|
|
||||||
<label for="page-number">Page:</label> <input type="text" size="3" id="page-number"> of <span id="number-pages"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
// Sample using dynamic pages with turn.js
|
|
||||||
|
|
||||||
var numberOfPages = 1000;
|
|
||||||
|
|
||||||
|
|
||||||
// Adds the pages that the book will need
|
|
||||||
function addPage(page, book) {
|
|
||||||
// First check if the page is already in the book
|
|
||||||
if (!book.turn('hasPage', page)) {
|
|
||||||
// Create an element for this page
|
|
||||||
var element = $('<div />', {'class': 'page '+((page%2==0) ? 'odd' : 'even'), 'id': 'page-'+page}).html('<i class="loader"></i>');
|
|
||||||
// If not then add the page
|
|
||||||
book.turn('addPage', element, page);
|
|
||||||
// Let's assum that the data is comming from the server and the request takes 1s.
|
|
||||||
setTimeout(function(){
|
|
||||||
element.html('<div class="data">Data for page '+page+'</div>');
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(window).ready(function(){
|
|
||||||
$('#book').turn({acceleration: true,
|
|
||||||
pages: numberOfPages,
|
|
||||||
elevation: 50,
|
|
||||||
gradients: !$.isTouch,
|
|
||||||
when: {
|
|
||||||
turning: function(e, page, view) {
|
|
||||||
|
|
||||||
// Gets the range of pages that the book needs right now
|
|
||||||
var range = $(this).turn('range', page);
|
|
||||||
|
|
||||||
// Check if each page is within the book
|
|
||||||
for (page = range[0]; page<=range[1]; page++)
|
|
||||||
addPage(page, $(this));
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
turned: function(e, page) {
|
|
||||||
$('#page-number').val(page);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#number-pages').html(numberOfPages);
|
|
||||||
|
|
||||||
$('#page-number').keydown(function(e){
|
|
||||||
|
|
||||||
if (e.keyCode==13)
|
|
||||||
$('#book').turn('page', $('#page-number').val());
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).bind('keydown', function(e){
|
|
||||||
|
|
||||||
if (e.target && e.target.tagName.toLowerCase()!='input')
|
|
||||||
if (e.keyCode==37)
|
|
||||||
$('#book').turn('previous');
|
|
||||||
else if (e.keyCode==39)
|
|
||||||
$('#book').turn('next');
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
@ -25,7 +25,8 @@ Compatibility is not yet guaranteed for every modern browser or device. Safari/W
|
|||||||
- restores original page elements as direct children;
|
- restores original page elements as direct children;
|
||||||
- restores original `style` and `class` attributes;
|
- restores original `style` and `class` attributes;
|
||||||
- removes turn.js internal jQuery data while preserving unrelated user data.
|
- removes turn.js internal jQuery data while preserving unrelated user data.
|
||||||
- Fixed `animatef(false)` so cancelling an animation clears the active interval.
|
- Replaced the internal `setInterval` animation loop with `requestAnimationFrame`.
|
||||||
|
- Fixed `animatef(false)` so cancelling an animation cancels the scheduled animation frame.
|
||||||
- Added a minimal browser demo using jQuery `4.0.0`.
|
- Added a minimal browser demo using jQuery `4.0.0`.
|
||||||
- Added text and local images to the minimal demo.
|
- Added text and local images to the minimal demo.
|
||||||
- Fixed minimal demo layout so turn.js page measurements match the real rendered page size.
|
- Fixed minimal demo layout so turn.js page measurements match the real rendered page size.
|
||||||
@ -167,6 +168,7 @@ The current suite covers:
|
|||||||
- dynamic `addPage` / `removePage`;
|
- dynamic `addPage` / `removePage`;
|
||||||
- event namespace registration;
|
- event namespace registration;
|
||||||
- `destroy` cleanup and re-initialization;
|
- `destroy` cleanup and re-initialization;
|
||||||
|
- `requestAnimationFrame` scheduling and cancellation in `animatef`;
|
||||||
- minimal demo rendering;
|
- minimal demo rendering;
|
||||||
- page turn rendering;
|
- page turn rendering;
|
||||||
- compact viewport geometry;
|
- compact viewport geometry;
|
||||||
|
|||||||
@ -11,7 +11,7 @@ const jqueryDist = dirname(require.resolve('jquery'));
|
|||||||
const jquerySource = readFileSync(resolve(jqueryDist, 'jquery.js'), 'utf8');
|
const jquerySource = readFileSync(resolve(jqueryDist, 'jquery.js'), 'utf8');
|
||||||
const turnSource = readFileSync(resolve(__dirname, '../../turn.js'), 'utf8');
|
const turnSource = readFileSync(resolve(__dirname, '../../turn.js'), 'utf8');
|
||||||
|
|
||||||
function createFixture(pageCount = 4) {
|
function createFixture(pageCount = 4, windowOptions = {}) {
|
||||||
const pages = Array.from({ length: pageCount }, (_, index) => (
|
const pages = Array.from({ length: pageCount }, (_, index) => (
|
||||||
`<div class="page">Page ${index + 1}</div>`
|
`<div class="page">Page ${index + 1}</div>`
|
||||||
)).join('');
|
)).join('');
|
||||||
@ -22,6 +22,13 @@ function createFixture(pageCount = 4) {
|
|||||||
url: 'http://localhost/'
|
url: 'http://localhost/'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.entries(windowOptions).forEach(([key, value]) => {
|
||||||
|
Object.defineProperty(dom.window, key, {
|
||||||
|
value,
|
||||||
|
configurable: true,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
});
|
||||||
dom.window.eval(jquerySource);
|
dom.window.eval(jquerySource);
|
||||||
const $ = dom.window.jQuery;
|
const $ = dom.window.jQuery;
|
||||||
dom.window.eval(turnSource);
|
dom.window.eval(turnSource);
|
||||||
@ -166,6 +173,53 @@ describe('turn.js jQuery plugin', () => {
|
|||||||
expect($book.find('.turn-page-wrapper').length).toBeGreaterThan(0);
|
expect($book.find('.turn-page-wrapper').length).toBeGreaterThan(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses requestAnimationFrame for animatef animations', () => {
|
||||||
|
fixture.window.close();
|
||||||
|
|
||||||
|
const frameCallbacks = [];
|
||||||
|
let frameHandle = 0;
|
||||||
|
const requestAnimationFrame = vi.fn(callback => {
|
||||||
|
frameCallbacks.push(callback);
|
||||||
|
frameHandle += 1;
|
||||||
|
return frameHandle;
|
||||||
|
});
|
||||||
|
const cancelAnimationFrame = vi.fn();
|
||||||
|
|
||||||
|
fixture = createFixture(4, {
|
||||||
|
performance: { now: vi.fn(() => 0) },
|
||||||
|
requestAnimationFrame,
|
||||||
|
cancelAnimationFrame
|
||||||
|
});
|
||||||
|
|
||||||
|
const { $ } = fixture;
|
||||||
|
const frames = [];
|
||||||
|
const complete = vi.fn();
|
||||||
|
const $element = $('<div></div>');
|
||||||
|
|
||||||
|
$element.animatef({
|
||||||
|
from: 0,
|
||||||
|
to: 10,
|
||||||
|
duration: 32,
|
||||||
|
easing: (x, t, b, c, d) => b + c * (t / d),
|
||||||
|
frame: value => frames.push(value),
|
||||||
|
complete
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(frames).toEqual([0]);
|
||||||
|
expect(requestAnimationFrame).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
frameCallbacks.shift()(16);
|
||||||
|
expect(frames[1]).toBe(5);
|
||||||
|
expect(requestAnimationFrame).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
$element.animatef(false);
|
||||||
|
expect(cancelAnimationFrame).toHaveBeenCalledWith(2);
|
||||||
|
|
||||||
|
frameCallbacks.shift()(32);
|
||||||
|
expect(frames).toEqual([0, 5]);
|
||||||
|
expect(complete).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('registers internal handlers with event namespaces', () => {
|
it('registers internal handlers with event namespaces', () => {
|
||||||
fixture.window.close();
|
fixture.window.close();
|
||||||
fixture = createFixture();
|
fixture = createFixture();
|
||||||
|
|||||||
44
turn.js
44
turn.js
@ -34,6 +34,20 @@ var has3d,
|
|||||||
events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'}
|
events = (isTouch) ? {start: 'touchstart', move: 'touchmove', end: 'touchend'}
|
||||||
: {start: 'mousedown', move: 'mousemove', end: 'mouseup'},
|
: {start: 'mousedown', move: 'mousemove', end: 'mouseup'},
|
||||||
|
|
||||||
|
frameTime = (window.performance && window.performance.now) ?
|
||||||
|
function() { return window.performance.now(); } :
|
||||||
|
function() { return new Date().getTime(); },
|
||||||
|
|
||||||
|
requestFrame = window.requestAnimationFrame ||
|
||||||
|
window.webkitRequestAnimationFrame ||
|
||||||
|
window.mozRequestAnimationFrame ||
|
||||||
|
function(callback) { return window.setTimeout(function() { callback(frameTime()); }, 16); },
|
||||||
|
|
||||||
|
cancelFrame = window.cancelAnimationFrame ||
|
||||||
|
window.webkitCancelAnimationFrame ||
|
||||||
|
window.mozCancelAnimationFrame ||
|
||||||
|
function(handle) { window.clearTimeout(handle); },
|
||||||
|
|
||||||
turnEventNamespace = '.turn',
|
turnEventNamespace = '.turn',
|
||||||
|
|
||||||
turnFlipEventNamespace = '.turnFlip',
|
turnFlipEventNamespace = '.turnFlip',
|
||||||
@ -2008,7 +2022,7 @@ $.extend($.fn, {
|
|||||||
var data = this.data();
|
var data = this.data();
|
||||||
|
|
||||||
if (data.effect)
|
if (data.effect)
|
||||||
clearInterval(data.effect.handle);
|
cancelFrame(data.effect.handle);
|
||||||
|
|
||||||
if (point) {
|
if (point) {
|
||||||
|
|
||||||
@ -2019,11 +2033,16 @@ $.extend($.fn, {
|
|||||||
var j, diff = [],
|
var j, diff = [],
|
||||||
len = point.to.length,
|
len = point.to.length,
|
||||||
that = this,
|
that = this,
|
||||||
fps = point.fps || 30,
|
startTime = frameTime(),
|
||||||
time = - fps,
|
f = function(timestamp) {
|
||||||
f = function() {
|
|
||||||
var j, v = [];
|
if (that.data().effect!==point)
|
||||||
time = Math.min(point.duration, time + fps);
|
return;
|
||||||
|
|
||||||
|
timestamp = timestamp || frameTime();
|
||||||
|
|
||||||
|
var time = Math.min(point.duration, Math.max(0, timestamp - startTime)),
|
||||||
|
j, v = [];
|
||||||
|
|
||||||
for (j = 0; j < len; j++)
|
for (j = 0; j < len; j++)
|
||||||
v.push(point.easing(1, time, point.from[j], diff[j], point.duration));
|
v.push(point.easing(1, time, point.from[j], diff[j], point.duration));
|
||||||
@ -2031,24 +2050,25 @@ $.extend($.fn, {
|
|||||||
point.frame((len==1) ? v[0] : v);
|
point.frame((len==1) ? v[0] : v);
|
||||||
|
|
||||||
if (time==point.duration) {
|
if (time==point.duration) {
|
||||||
clearInterval(data.effect.handle);
|
|
||||||
delete data['effect'];
|
delete data['effect'];
|
||||||
that.data(data);
|
that.data(data);
|
||||||
if (point.complete)
|
if (point.complete)
|
||||||
point.complete();
|
point.complete();
|
||||||
}
|
} else {
|
||||||
};
|
data.effect.handle = requestFrame(f);
|
||||||
|
that.data(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (j = 0; j < len; j++)
|
for (j = 0; j < len; j++)
|
||||||
diff.push(point.to[j] - point.from[j]);
|
diff.push(point.to[j] - point.from[j]);
|
||||||
|
|
||||||
data.effect = point;
|
data.effect = point;
|
||||||
data.effect.handle = setInterval(f, fps);
|
|
||||||
this.data(data);
|
this.data(data);
|
||||||
f();
|
f(startTime);
|
||||||
} else {
|
} else {
|
||||||
if (data.effect && data.effect.handle)
|
if (data.effect && data.effect.handle)
|
||||||
clearInterval(data.effect.handle);
|
cancelFrame(data.effect.handle);
|
||||||
delete data['effect'];
|
delete data['effect'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
turn.min.js
vendored
2
turn.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user