我想将图像(或元素)从其实际 X、Y 位置缓慢移动到 X1、Y1。
当X 和 X1 之间的距离等于Y 和 Y1 之间的距离时,它很容易。但是如果 X 差异是 100 像素而 Y 差异是 273 像素呢?
作为 Javascript 的新手,我不想重新发明轮子!此外,由于我正在学习,我不想使用 jQuery 或类似的东西。我想要纯javascript。
请提供简单的脚本:-)
我想将图像(或元素)从其实际 X、Y 位置缓慢移动到 X1、Y1。
当X 和 X1 之间的距离等于Y 和 Y1 之间的距离时,它很容易。但是如果 X 差异是 100 像素而 Y 差异是 273 像素呢?
作为 Javascript 的新手,我不想重新发明轮子!此外,由于我正在学习,我不想使用 jQuery 或类似的东西。我想要纯javascript。
请提供简单的脚本:-)
一种解决方案:
function translate( elem, x, y ) {
var left = parseInt( css( elem, 'left' ), 10 ),
top = parseInt( css( elem, 'top' ), 10 ),
dx = left - x,
dy = top - y,
i = 1,
count = 20,
delay = 20;
function loop() {
if ( i >= count ) { return; }
i += 1;
elem.style.left = ( left - ( dx * i / count ) ).toFixed( 0 ) + 'px';
elem.style.top = ( top - ( dy * i / count ) ).toFixed( 0 ) + 'px';
setTimeout( loop, delay );
}
loop();
}
function css( element, property ) {
return window.getComputedStyle( element, null ).getPropertyValue( property );
}
现场演示:http: //jsfiddle.net/qEVVT/1/
在具有各种不同功能(CPU、图形处理能力、计算机上正在发生的其他事情)的系统上制作流畅的动画并非易事。正确的实现涉及开发一种有效的“补间”算法,该算法可以自适应地(随着动画运行)计算出动画中要使用的增量,以便保持进度并尽可能平滑。
做到这一点的最好方法是站在别人的肩膀上,使用以前发明的东西。在这个时代,我永远不会尝试自己从头开始写这个。它可以在 CSS3 过渡/动画中使用,但尚未在所有地方都支持。它可以在 jQuery 和 YUI3 中使用或分析。我的第一选择是使用其中一个具有丰富功能的框架。您不必将框架用于其他任何事情,如果需要,您可以将其用于动画。YUI3 甚至可以让你构建一个代码最少的库来满足你的需求。jQuery 一开始并不是很大。
如果您仍然反对使用其中一个库,请将源代码下载到每个库的相关模块并研究它们是如何做到的。在每个应用程序中构建一个示例应用程序,并逐步了解它的工作原理,在有趣的地方设置断点。那将是最好的老师,向您展示如何构建一个有效的补间算法,以适应主机的速度能力。
为了让您了解补间算法如何用于直线动画(使用线性缓动),您需要对动画运行时间的动画步长值进行初始计算。这可能只是对系统可以支持什么的猜测。您将创建的步骤数划分为动画运行的时间,并为该时间量设置一个计时器,以便您知道何时运行下一步。然后,您运行一两步动画,您会看到实际经过了多少时间。如果计算机跟不上您的步数,您将落后于计划,您将不得不适应并选择更大的步数。
现在,如果你想做一些除了线性缓动之外的事情,显然还有更多的事情要做。
Firefox 和 Chrome 还实现了一些新的实验性 API 来帮助实现流畅的动画效果。我在查看 jQuery 源代码时自己发现了这一点,因为它在可用时使用它。在 Chrome 中,它被称为 webkitRequestAnimationFrame,您可以在 Firefox 博客文章中了解它。
如果您的目标是现代浏览器,CSS 过渡让生活更轻松(例如,对于 firefox,对于其他浏览器,更改 -moz 前缀):
<body>
<input type="button" onclick="move()" value="press" />
<div id="sq" style="position:absolute; top:50px; left:50px; height:50px; width:50px; background-color:Black; -moz-transition : all 0.8s ease 0s;" />
</body>
还有剧本
function move() {
var sq = document.getElementById("sq");
sq.style.left = "300px";
sq.style.top = "150px";
}
如果我要从头开始编写它,我会从以下内容开始:
function linearEase(start, end, percent) {
return start + ((end - start) * percent);
}
function animateTo(settings) {
var elem = settings.element;
var ease = settings.ease;
var start = { left: elem.offsetLeft, top: elem.offsetTop };
var lastTime = new Date().getTime();
var timeLeft = settings.totalTime;
function update() {
var currentTime = new Date().getTime();
var elapsed = currentTime - lastTime;
timeLeft -= elapsed;
lastTime = currentTime;
var percentDone = 1 - timeLeft/settings.totalTime;
elem.style.top = ease(start.top, settings.top, percentDone) + "px" ;
elem.style.left = ease(start.left, settings.left, percentDone) + "px" ;
if(timeLeft > 0) {
setTimeout(update, 33);
}
}
update();
}
例如,在接下来的两秒内将 div 移动到 (50,50)。
var elem = document.getElementById("animatable");
setTimeout(function() {
animateTo({
element: elem,
left: 50,
top: 50,
totalTime: 2000,
ease: linearEase
})
}, 10);
这是做这类事情的相当标准的模式。获取元素位置和设置样式的东西肯定可以更好地实现。但是从长远来看,抽象出一个ease
函数会让你的生活更轻松。我提供了一个简单的线性缓动,但其他更复杂的缓动算法将遵循相同的界面。
需要注意的另一件事是超时和间隔不能保证在设定的时间运行,因此通常最好设置您希望转换运行的总时间,然后计算自上次运行以来已经过去了多少时间你渲染的时间。
此外,如果您同时为一堆元素设置动画,我肯定会将其重构为单个“渲染循环”。对 的调用animateTo
会将工作人员推入工作人员队列,但只有setTimeout
循环计算经过的时间然后调用每个工作人员,因此您没有大量的timeout
闭包漂浮。
无论如何,在这里摆弄