我正在进行(1,0,0,-0.7,0.4320,70)的变换,我想逐渐结束于(1,0,0,1,0,0),我该怎么做?
这是转换图像的代码:
document.addEventListener("DOMContentLoaded", function(event) {
image = new Image();
image2 = new Image();
image3 = new Image();
image4 = new Image();
window.onload = function() {
//first image
var width = image.width,
height = image.height;
canvas1 = document.getElementById("num1Canvas");
bottomSlice = canvas1.getContext("2d");
//second image
var width2 = image2.width,
height2 = image2.height;
canvas2 = document.getElementById("num2Canvas");
topSlice = canvas2.getContext("2d");
//third image
newCanvas1 = document.getElementById("newNum1Canvas");
newBottomSlice = newCanvas1.getContext("2d");
//fourth image
newCanvas2 = document.getElementById("newNum2Canvas");
newTopSlice = newCanvas2.getContext("2d");
for (var i = 0; i <= height / 2; ++i) {
//first image transform
bottomSlice.setTransform(1, 0, -0.7, .4, 320, 70);
bottomSlice.drawImage(image,
0, height / 2 - i, width, 2,
0, height / 2 - i, width, 2);
bottomSlice.setTransform(1, 0, -0.7, 0.4, 320, 70);
bottomSlice.drawImage(image,
0, height / 2 + i, width, 2,
0, height / 2 + i, width, 2);
//second image transform
topSlice.setTransform(1, 0, -0.7, .4, 320, 0.2);
topSlice.drawImage(image2,
0, height2 / 2 - i, width2, 2,
0, height2 / 2 - i, width2, 2);
topSlice.setTransform(1, 0, -0.7, 0.4, 320, 0.2);
topSlice.drawImage(image2,
0, height2 / 2 + i, width2, 2,
0, height2 / 2 + i, width2, 2);
}
};
image.src = "bottom.png";
image2.src = "top.png";
image3.src = "bottom.png";//
image4.src ="top.png";
});
我基本上希望发生这样的事情:
function b(){
bottomSlice.clearRect(0, 0, canvas1.width, canvas1.height+60);
bottomSlice.setTransform(1, 0, -0.6, 0.3, 200, 40);
bottomSlice.drawImage(image, 0, 0);
bottomSlice.clearRect(0, 0, canvas1.width, canvas1.height+60);
bottomSlice.setTransform(1, 0, -0.4, 0.6, 150, 30);
bottomSlice.drawImage(image, 0, 0);
bottomSlice.clearRect(0, 0, canvas1.width, canvas1.height+60);
bottomSlice.setTransform(1, 0, -0.1, 0.8, 100, 20);
bottomSlice.drawImage(image, 0, 0);
bottomSlice.clearRect(0, 0, canvas1.width, canvas1.height+60);
bottomSlice.setTransform(1, 0, 0, 1, 0, 0);
bottomSlice.drawImage(image, 0, 0);
}
然而,上面的代码不起作用,而且是硬编码的,这不是我想要的。我想用setTimeout之类的东西来做这件事,但我不确定该怎么做
推文
大多数动画都涉及关键帧。最简单的是,你从一个状态开始,随着时间的推移,你会进入下一个状态。
简单粗花呢(又名lerp)
例如,我们有一些具有值和时间的关键帧。
var keys[ // time in seconds
{value : 10, time : 0},
{value : 20, time : 30},
}
在某些时候,我们想要值应该是什么。因此,忽略范围外的时间,我们可以编写一个简单的函数来获取给定时间的值。它首先将时间转换为关键帧之间的归一化时间(0到1),其中0是第一个关键帧处的时间,1是第二个关键帧的时间。
function lerpKeys(time, fromKey, toKey){
var relativeTime = time - fromKey.time;
var timeDiferance = toKey.time - fromKey.time;
var normalisedTime = relativeTime / timeDiferance;
var valueDiferance = toKey.value - fromKey.value;
var currentValue = valueDiferance * normalisedTime + fromKey.value;
return currentValue;
}
这是一个详细的例子,可以简化为
function lerpKeys (time, fromKey, toKey){
var nt = (time - fromKey.time) / (toKey.time - fromKey.time); // normalised time
return (toKey.value - fromKey.value) * nt + fromKey.value;
}
和缓解
这样做的美妙之处在于,归一化时间也可以应用许多缓和函数中的一个。缓和函数取0到1的值,并返回0到1之间的新值,但用曲线代替线性线。
缓解功能的一个例子
// ease in out
function ease (value, strength) { // strength is the amount of easing 1= no easing
// 1 < starts slow to fast then back to slow
// 0 < 1 fast to slow to fast
var v = Math.pow(Math.min(1, Math.max(0, value )), strength);
return v / (v + Math.pow(1 - value, strength));
}
// linear (no easing just clamps the value)
function linear (value){
return Math.max(0, Math.min(1, value));
}
要使用它(请注意,轻松功能将值钳制在0到1的范围内,这样,如果时间超出范围,动画将停止
// time in seconds
// from and to keys
// ease function to use
function lerpKeysEase(time, fromKey, toKey, easeFunc){
var nt = easeFunc((time - fromKey.time) / (toKey.time - fromKey.time), 2); // normalised time
return (toKey.value - fromKey.value) * nt + fromKey.value;
}
var currentValue = lerpKeysEase(time, keys[0], keys[1], ease);
大多数常见的宽松功能可以在Github简单宽松中找到
更新
哦,我应该在发布之前阅读上面的github页面,因为这些函数只是上面片段中easy-in-out函数的变体。一个优秀的放松功能页面放松示例和代码,以及另一个快速视觉放松参考页面
转换
因此,这是粗花呢的基础,很容易适应位置和变换等情况。
// keys with transforms
var keys[ // time in seconds
{value : [1, 0, -0.7, 0.4, 320, 70] , time : 0},
{value : [1, 0, 0, 1, 0, 0] , time : 30},
}
// lerp the transform
function lerpTranformKeysEase(time, fromKey, toKey, easeFunc){
var f = fromKey.value; // to save some typing
var t = toKey.value;
var i = 0;
var nt = easeFunc((time - fromKey.time) / (toKey.time - fromKey.time), 2); // normalised time
// return an array with the new transform
return [
(t[i] - f[i]) * nt + f[i++],
(t[i] - f[i]) * nt + f[i++],
(t[i] - f[i]) * nt + f[i++],
(t[i] - f[i]) * nt + f[i++],
(t[i] - f[i]) * nt + f[i++],
(t[i] - f[i]) * nt + f[i++]
];
}
一切都很简单,真的。。。但是。。。。
一个更好的lerp(修复缺陷)
变换矩阵具有一些特殊的性质,并且对许多事情进行编码,如位置、旋转、缩放X/Y、偏斜、剪切和应用简单的lerp(tween)函数将不会得到正确的结果,因为被变换的对象将变形。
你需要做的是将变换分解成一个更有用的形式。这简单的意思是进行一个普通的变换,并返回我们想要设置动画的各种编码值。变换有三个基本部分,即X轴(前两个值)、Y轴(后两个值")和原点(最后两个)。X,Y轴有一个方向和比例,原点只是一个简单的坐标。
因此,让我们创建一个分解函数,返回一个数组,该数组包含x,y轴方向、x,y比例和原点
// NOTE Math.hypot is not supported on all browsers use
// Math.sqrt(matrix[1] * matrix[1] + matrix[0] * matrix[0])
// if needed
function matDecompose(matrix){ // matrix as array
return [
Math.atan2(matrix[1], matrix[0]), // x axis direction
Math.atan2(matrix[3], matrix[2]), // y axis direction
Math.hypot(matrix[1], matrix[0]), // x axis scale
Math.hypot(matrix[3], matrix[2]), // y axis scale
matrix[4], matrix[5] // origin
];
}
现在我们有了这个函数,我们可以将变换转换为分解的值,并将它们放在密钥中
var keys[ // time in seconds
{value : matDecompose([1, 0, -0.7, 0.4, 320, 70]) , time : 0},
{value : matDecompose([1, 0, 0, 1, 0, 0]) , time : 30},
}
然后你只需要像以前一样在关键点之间切换,但这一次旋转、缩放和倾斜将更准确地切换。
当然,分解后的矩阵没有用处,所以我们需要将其转换回可用的变换矩阵。
// Returns a matrix as array from the decomposed matrix
function reCompose(m) { // m is the decomposed matrix as array
return [
Math.cos(m[0]) * m[2], // reconstruct X axis and scale
Math.sin(m[0]) * m[2],
Math.cos(m[1]) * m[3], // reconstruct Y axis and scale
Math.sin(m[1]) * m[3],
m[4], m[5] // origin
];
}
现在,您可以应用tween来获得新的(分解的变换),并将其转换回标准矩阵以应用于画布
var currentMatrix = reCompose( lerpTranformKeysEase(time, keys[0], keys[1], ease));
ctx.setTransform(
currentMatrix[0],
currentMatrix[1],
currentMatrix[2],
currentMatrix[3],
currentMatrix[4],
currentMatrix[5]
);
// Now render the object.
现在,您可以开始使用一个非常方便的关键帧动画界面了。多个关键帧有了更多的逻辑,你就可以随心所欲地制作任何动画,现在的问题是从哪里获得关键帧????
我使用了您的一些代码并添加了一个动画循环。您可以使用setTimeout代替requestAnimationFrame。setTimeout(animationLoop,毫秒);
document.addEventListener("DOMContentLoaded", function(event) {
image = new Image();
image2 = new Image();
image3 = new Image();
image4 = new Image();
window.onload = function() {
//first image
var width = image.width,
height = image.height;
canvas1 = document.getElementById("num1Canvas");
bottomSlice = canvas1.getContext("2d");
//second image
var width2 = image2.width,
height2 = image2.height;
canvas2 = document.getElementById("num2Canvas");
topSlice = canvas2.getContext("2d");
//third image
newCanvas1 = document.getElementById("newNum1Canvas");
newBottomSlice = newCanvas1.getContext("2d");
//fourth image
newCanvas2 = document.getElementById("newNum2Canvas");
newTopSlice = newCanvas2.getContext("2d");
var i = 0;
function animationLoop() {
if (i > height / 2) {
alert('done!');
return;
}
//first image transform
bottomSlice.setTransform(1, 0, -0.7, .4, 320, 70);
bottomSlice.drawImage(image,
0, height / 2 - i, width, 2,
0, height / 2 - i, width, 2);
bottomSlice.setTransform(1, 0, -0.7, 0.4, 320, 70);
bottomSlice.drawImage(image2,
0, height / 2 + i, width, 2,
0, height / 2 + i, width, 2);
//second image transform
topSlice.setTransform(1, 0, -0.7, .4, 320, 0.2);
topSlice.drawImage(image3,
0, height2 / 2 - i, width2, 2,
0, height2 / 2 - i, width2, 2);
topSlice.setTransform(1, 0, -0.7, 0.4, 320, 0.2);
topSlice.drawImage(image4,
0, height2 / 2 + i, width2, 2,
0, height2 / 2 + i, width2, 2);
i++;
requestAnimationFrame(animationLoop);
}
animationLoop();
};
var can = document.createElement('canvas');
var w = can.width=300;
var h = can.height=300;
var ctx = can.getContext('2d');
ctx.fillStyle="red";
ctx.fillRect(0,0,w,h);
image.src = can.toDataURL("image/png");//"bottom.png";
ctx.fillStyle="blue";
ctx.fillRect(0,0,w,h);
image2.src = can.toDataURL("image/png");//"top.png";
ctx.fillStyle="green";
ctx.fillRect(0,0,w,h);
image3.src = can.toDataURL("image/png");//"bottom.png";
ctx.fillStyle="black";
ctx.fillRect(0,0,w,h);
image4.src = can.toDataURL("image/png");//"top.png";
});
<canvas id="num1Canvas"></canvas>
<canvas id="num2Canvas"></canvas>
<canvas id="newNum1Canvas"></canvas>
<canvas id="newNum2Canvas"></canvas>