extraction du calcul du pli dans une fonction interne pure

This commit is contained in:
poongaloo 2026-06-08 22:37:51 +02:00
parent a5bf642e07
commit 688034f7d3
3 changed files with 228 additions and 90 deletions

View File

@ -78,6 +78,82 @@ describe('turn.js jQuery plugin', () => {
expect($.fn.transform).toBeTypeOf('function');
});
it('calculates fold geometry without DOM for every corner', () => {
fixture.window.close();
fixture = createFixture(4, {
__TURNJS_TEST_HOOKS__: {}
});
const calculateFoldGeometry = fixture.window.__TURNJS_TEST_HOOKS__.calculateFoldGeometry;
const base = {
width: 300,
height: 400,
wrapperHeight: 500,
frontGradient: true,
backGradient: true
};
const cases = [
{
point: { corner: 'tl', x: 90, y: 120 },
origin: { x: 0, y: 0 },
endingPoint: { x: 600, y: 0 },
top: true,
left: true
},
{
point: { corner: 'tr', x: 210, y: 120 },
origin: { x: 300, y: 0 },
endingPoint: { x: -300, y: 0 },
top: true,
left: false
},
{
point: { corner: 'bl', x: 90, y: 280 },
origin: { x: 0, y: 400 },
endingPoint: { x: 600, y: 400 },
top: false,
left: true
},
{
point: { corner: 'br', x: 210, y: 280 },
origin: { x: 300, y: 400 },
endingPoint: { x: -300, y: 400 },
top: false,
left: false
}
];
expect(calculateFoldGeometry).toBeTypeOf('function');
for (const item of cases) {
const originalPoint = { ...item.point };
const geometry = calculateFoldGeometry({ ...base, ...item });
expect(item.point).toEqual(originalPoint);
expect(geometry.point.corner).toBe(item.point.corner);
expect(geometry.top).toBe(item.top);
expect(geometry.left).toBe(item.left);
expect(Number.isFinite(geometry.alpha)).toBe(true);
expect(Number.isFinite(geometry.angle)).toBe(true);
expect(Number.isFinite(geometry.px)).toBe(true);
expect(Number.isFinite(geometry.tr.x)).toBe(true);
expect(Number.isFinite(geometry.tr.y)).toBe(true);
expect(Number.isFinite(geometry.move.x)).toBe(true);
expect(Number.isFinite(geometry.move.y)).toBe(true);
expect(Number.isFinite(geometry.df.x)).toBe(true);
expect(Number.isFinite(geometry.df.y)).toBe(true);
expect(Number.isFinite(geometry.gradient.opacity)).toBe(true);
expect(Number.isFinite(geometry.gradient.size)).toBe(true);
expect(geometry.gradient.endPointA).toBeDefined();
expect(geometry.gradient.endPointB).toBeDefined();
if (item.left)
expect(geometry.point.x).toBeGreaterThanOrEqual(1);
else
expect(geometry.point.x).toBeLessThanOrEqual(base.width - 1);
}
});
it('initializes a book and exposes page, view, pages and size state', () => {
const { $ } = fixture;
const $book = $('#book');

240
turn.js
View File

@ -218,6 +218,127 @@ var has3d,
return vendor;
},
// Calculates the fold geometry without touching the DOM
calculateFoldGeometry = function(opts) {
var alpha = 0,
angle = 0,
px,
gradientEndPointA,
gradientEndPointB,
gradientStartV,
gradientSize,
gradientOpacity,
mv = point2D(0, 0),
df = point2D(0, 0),
tr = point2D(0, 0),
width = opts.width,
height = opts.height,
h = opts.wrapperHeight,
o = opts.origin,
point = point2D(opts.point.x, opts.point.y),
top = opts.point.corner.substr(0, 1) == 't',
left = opts.point.corner.substr(1, 1) == 'l';
point.corner = opts.point.corner;
if (left)
point.x = Math.max(point.x, 1);
else
point.x = Math.min(point.x, width-1);
var compute = function() {
var rel = point2D((o.x) ? o.x - point.x : point.x, (o.y) ? o.y - point.y : point.y),
tan = (Math.atan2(rel.y, rel.x)),
middle;
alpha = A90 - tan;
angle = deg(alpha);
middle = point2D((left) ? width - rel.x/2 : point.x + rel.x/2, rel.y/2);
var gamma = alpha - Math.atan2(middle.y, middle.x),
distance = Math.max(0, Math.sin(gamma) * Math.sqrt(Math.pow(middle.x, 2) + Math.pow(middle.y, 2)));
tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha));
if (alpha > A90) {
tr.x = tr.x + Math.abs(tr.y * Math.tan(tan));
tr.y = 0;
if (Math.round(tr.x*Math.tan(PI-alpha)) < height) {
point.y = Math.sqrt(Math.pow(height, 2)+2 * middle.x * rel.x);
if (top) point.y = height - point.y;
return compute();
}
}
if (alpha>A90) {
var beta = PI-alpha, dd = h - height/Math.sin(beta);
mv = point2D(Math.round(dd*Math.cos(beta)), Math.round(dd*Math.sin(beta)));
if (left) mv.x = - mv.x;
if (top) mv.y = - mv.y;
}
px = Math.round(tr.y/Math.tan(alpha) + tr.x);
var side = width - px,
sideX = side*Math.cos(alpha*2),
sideY = side*Math.sin(alpha*2);
df = point2D(Math.round( (left ? side -sideX : px+sideX)), Math.round((top) ? sideY : height - sideY));
gradientSize = side*Math.sin(alpha);
var far = Math.sqrt(Math.pow(opts.endingPoint.x-point.x, 2)+Math.pow(opts.endingPoint.y-point.y, 2));
gradientOpacity = (far<width) ? far/width : 1;
if (opts.frontGradient) {
gradientStartV = gradientSize>100 ? (gradientSize-100)/gradientSize : 0;
gradientEndPointA = point2D(gradientSize*Math.sin(A90-alpha)/height*100, gradientSize*Math.cos(A90-alpha)/width*100);
if (top) gradientEndPointA.y = 100-gradientEndPointA.y;
if (left) gradientEndPointA.x = 100-gradientEndPointA.x;
}
if (opts.backGradient) {
gradientEndPointB = point2D(gradientSize*Math.sin(alpha)/width*100, gradientSize*Math.cos(alpha)/height*100);
if (!left) gradientEndPointB.x = 100-gradientEndPointB.x;
if (!top) gradientEndPointB.y = 100-gradientEndPointB.y;
}
tr.x = Math.round(tr.x);
tr.y = Math.round(tr.y);
return true;
};
compute();
return {
point: point,
alpha: alpha,
angle: angle,
tr: tr,
move: mv,
df: df,
px: px,
top: top,
left: left,
gradient: {
endPointA: gradientEndPointA,
endPointB: gradientEndPointB,
startV: gradientStartV,
size: gradientSize,
opacity: gradientOpacity
}
};
},
// Adds gradients
gradient = function(obj, p0, p1, colors, numColors) {
@ -1593,12 +1714,9 @@ flipMethods = {
var that = this,
a = 0,
alpha = 0,
beta,
px,
gradientEndPointA,
gradientEndPointB,
gradientStartV,
gradientSize,
gradientOpacity,
mv = point2D(0, 0),
df = point2D(0, 0),
@ -1606,89 +1724,24 @@ flipMethods = {
width = this.width(),
height = this.height(),
folding = flipMethods._foldingPage.call(this),
tan = Math.tan(alpha),
data = this.data().f,
ac = data.opts.acceleration,
h = data.wrapper.height(),
o = flipMethods._c.call(this, point.corner),
backGradient = flipMethods._backGradient.call(this),
geometry = calculateFoldGeometry({
point: point,
width: width,
height: height,
wrapperHeight: h,
origin: o,
endingPoint: flipMethods._c2.call(this, point.corner),
frontGradient: data.opts.frontGradient,
backGradient: backGradient
}),
top = point.corner.substr(0, 1) == 't',
left = point.corner.substr(1, 1) == 'l',
compute = function() {
var rel = point2D((o.x) ? o.x - point.x : point.x, (o.y) ? o.y - point.y : point.y),
tan = (Math.atan2(rel.y, rel.x)),
middle;
alpha = A90 - tan;
a = deg(alpha);
middle = point2D((left) ? width - rel.x/2 : point.x + rel.x/2, rel.y/2);
var gamma = alpha - Math.atan2(middle.y, middle.x),
distance = Math.max(0, Math.sin(gamma) * Math.sqrt(Math.pow(middle.x, 2) + Math.pow(middle.y, 2)));
tr = point2D(distance * Math.sin(alpha), distance * Math.cos(alpha));
if (alpha > A90) {
tr.x = tr.x + Math.abs(tr.y * Math.tan(tan));
tr.y = 0;
if (Math.round(tr.x*Math.tan(PI-alpha)) < height) {
point.y = Math.sqrt(Math.pow(height, 2)+2 * middle.x * rel.x);
if (top) point.y = height - point.y;
return compute();
}
}
if (alpha>A90) {
var beta = PI-alpha, dd = h - height/Math.sin(beta);
mv = point2D(Math.round(dd*Math.cos(beta)), Math.round(dd*Math.sin(beta)));
if (left) mv.x = - mv.x;
if (top) mv.y = - mv.y;
}
px = Math.round(tr.y/Math.tan(alpha) + tr.x);
var side = width - px,
sideX = side*Math.cos(alpha*2),
sideY = side*Math.sin(alpha*2);
df = point2D(Math.round( (left ? side -sideX : px+sideX)), Math.round((top) ? sideY : height - sideY));
// GRADIENTS
gradientSize = side*Math.sin(alpha);
var endingPoint = flipMethods._c2.call(that, point.corner),
far = Math.sqrt(Math.pow(endingPoint.x-point.x, 2)+Math.pow(endingPoint.y-point.y, 2));
gradientOpacity = (far<width) ? far/width : 1;
if (data.opts.frontGradient) {
gradientStartV = gradientSize>100 ? (gradientSize-100)/gradientSize : 0;
gradientEndPointA = point2D(gradientSize*Math.sin(A90-alpha)/height*100, gradientSize*Math.cos(A90-alpha)/width*100);
if (top) gradientEndPointA.y = 100-gradientEndPointA.y;
if (left) gradientEndPointA.x = 100-gradientEndPointA.x;
}
if (flipMethods._backGradient.call(that)) {
gradientEndPointB = point2D(gradientSize*Math.sin(alpha)/width*100, gradientSize*Math.cos(alpha)/height*100);
if (!left) gradientEndPointB.x = 100-gradientEndPointB.x;
if (!top) gradientEndPointB.y = 100-gradientEndPointB.y;
}
//
tr.x = Math.round(tr.x);
tr.y = Math.round(tr.y);
return true;
},
transform = function(tr, c, x, a) {
var f = ['0', 'auto'], mvW = (width-h)*x[0]/100, mvH = (height-h)*x[1]/100,
@ -1715,7 +1768,7 @@ flipMethods = {
3,
alpha);
if (flipMethods._backGradient.call(that))
if (backGradient)
gradient(data.bshadow,
point2D(left?0:100, top?0:100),
point2D(gradientEndPointB.x, gradientEndPointB.y),
@ -1723,34 +1776,40 @@ flipMethods = {
[0.9, 'rgba(0,0,0,'+(0.18*gradientOpacity)+')'],
[1, 'rgba(0,0,0,0)']],
3);
};
point.x = geometry.point.x;
point.y = geometry.point.y;
alpha = geometry.alpha;
a = geometry.angle;
tr = geometry.tr;
mv = geometry.move;
df = geometry.df;
top = geometry.top;
left = geometry.left;
gradientEndPointA = geometry.gradient.endPointA;
gradientEndPointB = geometry.gradient.endPointB;
gradientStartV = geometry.gradient.startV;
gradientOpacity = geometry.gradient.opacity;
switch (point.corner) {
case 'tl' :
point.x = Math.max(point.x, 1);
compute();
transform(tr, [1,0,0,1], [100, 0], a);
data.fpage.transform(translate(-height, -width, ac) + rotate(90-a*2) , '100% 100%');
folding.transform(rotate(90) + translate(0, -height, ac), '0% 0%');
break;
case 'tr' :
point.x = Math.min(point.x, width-1);
compute();
transform(point2D(-tr.x, tr.y), [0,0,0,1], [0, 0], -a);
data.fpage.transform(translate(0, -width, ac) + rotate(-90+a*2) , '0% 100%');
folding.transform(rotate(270) + translate(-width, 0, ac), '0% 0%');
break;
case 'bl' :
point.x = Math.max(point.x, 1);
compute();
transform(point2D(tr.x, -tr.y), [1,1,0,0], [100, 100], -a);
data.fpage.transform(translate(-height, 0, ac) + rotate(-90+a*2), '100% 0%');
folding.transform(rotate(270) + translate(-width, 0, ac), '0% 0%');
break;
case 'br' :
point.x = Math.min(point.x, width-1);
compute();
transform(point2D(-tr.x, -tr.y), [0,1,1,0], [0, 100], a);
data.fpage.transform(rotate(90-a*2), '0% 0%');
folding.transform(rotate(90) + translate(0, -height, ac), '0% 0%');
@ -2104,6 +2163,9 @@ $.extend($.fn, {
});
if (window.__TURNJS_TEST_HOOKS__)
window.__TURNJS_TEST_HOOKS__.calculateFoldGeometry = calculateFoldGeometry;
$.isTouch = isTouch;
})(jQuery);

2
turn.min.js vendored

File diff suppressed because one or more lines are too long