【问题标题】:Ease in/out camera movement for canvas画布的缓入/缓出相机移动
【发布时间】:2015-09-18 16:52:54
【问题描述】:

我有一个函数可以将相机移动到用户在画布上单击的位置。但目前这种运动是线性的。我希望它使用缓入/缓出过渡,但我很难理解如何使用我当前的功能来实现它。

这是我的代码:

animate.mouseEvent = function(e,el){        
    var mousePos = mouse.relativePosition(e,el);
    var parent   = el;
    if(e.button == 0){

            function update(){
                if(centerX == mousePos.x && centerY == mousePos.y){
                    clearInterval(parent.timer);
                    //center X / Y is canvas width & height halved
                }

                var difx            = centerX - mousePos.x,
                    dify            = centerY - mousePos.y,
                    distance        = Math.sqrt(difx*difx + dify*dify),
                    normalX         = difx/distance,
                    normalY         = dify/distance,
                    speed           = 1, //currently linear [replace with ease in/out]
                    x               = normalX * speed,
                    y               = normalY * speed;

                    updateOffsets(x,y);                     
                    mousePos.x  += x;
                    mousePos.y  += y;
                }
                parent.timer = setInterval(update,1);   
     }
}


animate.updateOffsets = function(x,y){
    canvas.offsetX -= x;
    canvas.offsetY -= y;
}

所以我想知道如何从我当前的线性方法中实现缓入/缓出。使用以下函数:

Math.easeInOutQuad = function (t, b, c, d) {
    t /= d/2;
    if (t < 1) return c/2*t*t + b;
    t--;
    return -c/2 * (t*(t-2) - 1) + b;
};

我不完全知道如何实现它,也不知道它究竟会返回什么来计算新的偏移量。希望有人能解释一下。

【问题讨论】:

    标签: javascript


    【解决方案1】:

    这应该会给你一个想法。我使用了另一个缓动函数,你可以替换你自己的。 Example

    var element = document.getElementById('moving');
    var bg = document.getElementById('background');
    
    bg.addEventListener('click', function(evt) {
      animate({
        x: evt.offsetX,
        y: evt.offsetY
      });
    }, false);
    
    function getPosition() {
      return {
        x: element.offsetLeft,
        y: element.offsetTop
      };
    }
    function setPosition(x, y) {
      element.style.left = x + 'px';
      element.style.top = y + 'px';
    }
    
    function easing(x) {
      return 0.5 + 0.5 * Math.sin((x - 0.5) * Math.PI);
    }
    
    function animate(target) {
      var initial = getPosition();
      var initialX = initial.x;
      var initialY = initial.y;
      var targetX = target.x;
      var targetY = target.y;
      var deltaX = targetX - initialX;
      var deltaY = targetY - initialY;
    
      var timeStart = timestamp();
      var timeLength = 800;
    
      var timer = setInterval(update, 10);
    
      function timestamp() {
        return Date.now();
      }
      function stop() {
        clearInterval(timer);
      }
    
      function update() {
        var t = (timestamp() - timeStart) / timeLength;
        if (t > 1) {
          fraction(1);
          stop();
        } else {
          fraction(easing(t));
        }
      }
      function fraction(t) {
        setPosition(initialX + t * deltaX, initialY + t * deltaY);
      }
    }
    

    编辑

    应用于您提供的fiddle

    var element         = document.getElementById('background');
    var ctx             = element.getContext("2d");
    var camera          = {};
        // camera.offset marks the position seen at the upper left corner
        // (It would be better if it marked the center)
        // Let these be negative, so (0,0) scene position is at the center of the image
        camera.offsetX  = -element.width/2;
        camera.offsetY  = -element.height/2;
    var obj             = {};
        obj.x           = 50;
        obj.y           = 50;
    
    element.addEventListener('click', animate, false);
    
    function easing(x) {
          return 0.5 + 0.5 * Math.sin((x - 0.5) * Math.PI);
    }
    
    function animate(evt){
        // Transform click position from screen coordinates to scene coordinates
      var targetX       = evt.offsetX + camera.offsetX - element.width / 2,
          targetY       = evt.offsetY + camera.offsetY - element.height / 2;
    
      var initialX      = camera.offsetX,
        initialY        = camera.offsetY;
    
      var deltaX        = targetX - initialX,
          deltaY        = targetY - initialY;
    
      var timeStart     = Date.now();
      var timeLength    = 800;
      var timer         = setInterval(update, 10);
    
      function stop(){
        clearInterval(timer);
      }
    
      function update(){
        var t = (Date.now() - timeStart) / timeLength;
          if (t > 1) {
              fraction(1);
              stop();
          } else {
              fraction(easing(t));
          }
      }    
      function fraction(t){
          camera.offsetX = initialX + t * deltaX,
          camera.offsetY = initialY + t * deltaY;
      }
    }
    
    function draw(){
        ctx.clearRect(0,0,element.width,element.height);
    
      ctx.fillStyle = 'red';
      ctx.fillRect((element.width/2)-2,(element.height/2)-2,4,4);       
    
        ctx.fillStyle = 'blue';
        // ===> Here the size and position arguments were swapped
      ctx.fillRect(obj.x-camera.offsetX,obj.y-camera.offsetY, 20, 20);   
    }
    
    // Consider using requestAnimationFrame
    setInterval(draw, 10);
    

    【讨论】:

    • 这是否使用缓入/缓出?
    • 是的,确实如此,类似正弦的缓动,有点难以注意到。处理盒子的坐标是不准确的。缓动函数应该是带有easing(0)=0 and easing(1)=1 的函数,并且如果easing'(0)=easing'(1)=0 将是平滑的(导数在0 和1 处为零)尝试摆弄缓动fn
    • 我试图在此处实现这种移动相机的方法:jsfiddle.net/b1v7pe3r 但由于某种原因我无法让它工作。我哪里做错了?
    • 这主要是错误的,因为你在蓝色的东西上交换了fillRect的参数,并绘制了一个-250宽度的矩形。查看我的编辑
    • 为什么我在摆弄这段代码并做了这个:jsfiddle.net/hsrvh0L2我改变了相机的工作方式,所以它指的是屏幕中间而不是左上角,遗憾的是现在动画坏了我不知道为什么或如何解决它。
    猜你喜欢
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    相关资源
    最近更新 更多