我想知道如何通过animate
调用requestAnimationFrame
函数,只有在真正需要它时才会这样做。目前,animate
始终被调用,这会产生一些开销。
我已在我的animate
函数中尝试比较targetRadius
和初始radius
,并在它们相同时返回false。不幸的是,这根本不起作用。
有人可以解释一下如何解决这个问题吗?
HTML:
<canvas id="ddayCanvas" width="288" height="288" data-image="http://www.topdesignmag.com/wp-content/uploads/2011/07/64.png">
<div>
<div class="product-image"></div>
<div class="product-box">...</div>
<a href="#" class="overlay">...</a>
</div>
</canvas>
JS:
// Options
var maxImageWidth = 250,
maxImageHeight = 196;
var canvas = $('#ddayCanvas'),
canvasWidth = canvas.width(),
canvasHeight = canvas.height(),
sectorColor = $('.product-box').css('background-color'),
context = canvas[0].getContext('2d'),
imageSrc = canvas.data('image'),
imageObj = new Image(),
imageWidth, imageHeight,
mouseover = false;
imageObj.onload = function() {
imageWidth = this.width;
imageHeight = this.height;
if (imageWidth > maxImageWidth){
imageHeight = imageHeight - (imageWidth - maxImageWidth);
imageWidth = maxImageWidth;
}
if (imageHeight > maxImageHeight) {
imageWidth = imageWidth - (imageHeight - maxImageHeight);
imageHeight = maxImageHeight;
}
drawDday(90);
};
imageObj.src = imageSrc;
function drawDday (radius) {
context.clearRect(0, 0, canvasWidth, canvasHeight);
context.drawImage(imageObj, Math.ceil((canvasWidth - imageWidth) / 2), Math.ceil((canvasHeight - imageHeight) / 2), imageWidth, imageHeight);
context.fillStyle = sectorColor;
context.beginPath();
context.rect(0, 0, canvasWidth, canvasHeight);
context.arc(canvasWidth/2, canvasHeight/2, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
// Check out the console
console.log('test');
}
var radius = baseRadius = 90,
targetRadius = 110,
ease = 50,
speed = 2;
function animate(){
if(mouseover){
radius += ((targetRadius-radius)/ease)*speed;
} else {
radius -= ((radius-baseRadius)/ease)*speed;
}
if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;
drawDday(radius);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
canvas.on('mouseover', function(e){
mouseover = true;
}).on('mouseout', function(){
mouseover = false;
});
答案 0 :(得分:5)
您需要实现条件,以便打破循环,例如(根据需要采用):
var isRunning = true;
function loop() {
... funky stuff here ...
/// test condition before looping
if (isRunning) requestAnimationFrame(loop);
}
现在,当您将isRunning
设置为false
时,循环将会中断。为方便起见,建议您使用方法来启动和停止循环:
function startLoop(state) {
if (state && !isRunning) {
isRunning = true;
loop(); /// starts loop
} else if (!state && isRunning) {
isRunning = false;
}
}
条件可以由你需要设置的任何东西来设置,例如在动画完成后的回调等等。重要的部分是条件标志可用于使用它的两个范围(即最常见的)在全球范围内)。
<强>更新强>:
在这种情况下更具体的是你的条件(半径)永远不会达到最终停止循环所需的条件。
您可以采取以下措施来解决此问题:
var isPlaying = false;
function animate(){
/**
* To make sure you will reach the condition required you need
* to either make sure you have a fall-out for the steps or the
* step will become 0 not adding/subtracting anything so your
* checks below won't trigger. Here we can use a simple max of
* the step and a static value to make sure the value is always > 0
*/
if(mouseover){
radius += Math.max( ((targetRadius-radius)/ease)*speed, 0.5);
} else {
radius -= Math.max( ((radius-baseRadius)/ease)*speed, 0.5);
}
/**
* Now the checks will trigger properly and we can use the
* isPlaying flag to stop the loop when targets are reached.
*/
if(radius >= targetRadius) {
radius = targetRadius;
isPlaying = false; /// stop loop after this
} else if (radius <= baseRadius) {
radius = baseRadius;
isPlaying = false; /// stop loop after this
}
drawDday(radius);
/// loop?
if (isPlaying === true) requestAnimationFrame(animate);
}
为了触发循环,我们使用一种方法来检查循环是否正在运行,如果不是,它将重置isPlaying
标志并启动循环。我们在mouseover
和mouseout
:
canvas.on('mouseover', function(e){
mouseover = true;
startAnim();
}).on('mouseout', function(){
mouseover = false;
startAnim();
});
该方法只是检查isPlaying
,如果没有设置,则将其设置为true并启动循环 - 这样循环只启动一次:
function startAnim() {
if (!isPlaying) {
isPlaying = true;
requestAnimationFrame(animate);
}
}
在演示中,我添加了控制台日志记录,以显示循环何时运行以及何时命中目标。
希望这有帮助。
答案 1 :(得分:3)
您的animate
功能被连续呼叫的原因是,您首先拨打requestAnimationFrame(animate);
,然后每次拨打animate
,再次无条件呼叫requestAnimationFrame(animate);
。除非你在某个时刻使用cancelAnimationFrame
(你没有),否则这个周期永远不会被打破,或者确保animate
仅在需要时请求另一个帧。
另一个问题是,radius
目前永远不会到达targetRadius
或baseRadius
,因此以下任何一项都不会成立:
if(radius > targetRadius) radius = targetRadius;
if(radius < baseRadius) radius = baseRadius;
这不是对animate
的持续调用的直接责任,但由于targetRadius
和baseRadius
用于指示动画的结束点,因此我们需要形成他们有某种明智的条件。
所以,你可以这样做:http://jsfiddle.net/PLDUq/9/
var radius = baseRadius = 50,
targetRadius = 110,
ease = 50,
speed = 12,
currentAnim;
function animate(){
if(mouseover){
radius += ((targetRadius-radius)/ease)*speed;
} else {
radius -= ((radius-baseRadius)/ease)*speed;
}
drawDday(radius);
if(Math.round(radius) >= targetRadius) {
// uses Math.round() to ensure condition can be fulfilled
radius = targetRadius;
return; // doesn't call next frame
}
if(Math.round(radius) <= baseRadius) {
radius = baseRadius;
return; // doesn't call next frame
}
requestAnimationFrame(animate);
}
canvas.on('mouseenter mouseleave', function (e) {
if (currentAnim) {requestAnimationFrame(currentAnim);}
// cancels current animation if one is playing to
// prevent several concurrent loops calling animate()
mouseover = (e.type === 'mouseenter');
requestAnimationFrame(animate);
});