如何在窗口画布中移动图像的效果?

时间:2017-06-25 15:38:09

标签: javascript css html5 canvas

例如,我的画布有限,宽度/高度比上传的图像小。 伙计们如何在画布窗口中产生移动图像的效果?换句话说,画布窗口不会改变,而且我们“运行”的画面也不会改变。感谢

enter image description here

2 个答案:

答案 0 :(得分:0)

查看此答案:https://stackoverflow.com/a/30739547/3200577

基本上,html canvas的工作方式与html元素的渲染和绘制方式有很大不同。虽然您可以在页面上选择并移动html元素,但您无法选择并移动已添加到画布中的内容,因为您可以在画布上执行的操作是添加和清除像素。因此,当您将图像添加到画布时,您将图像的像素添加到画布。如果您要再次添加图像但稍微向左添加图像,那么看起来您已经添加了两个图像,其中第二个图像与第一个图像重叠,这不是您想要的。

因此,要在画布上为图像的运动设置动画,您需要:

  • 选择x和y作为画布上图像的位置
  • 在画布上以x和y
  • 绘制图像
  • 将x和y的值增加到您想要的新位置
  • 清除画布
  • 在新的x和y
  • 处重绘图像

对此流程的更抽象的描述:基本上,您需要创建,存储和管理您自己的模型画布的外观;当您想添加,删除您在画布上绘制的更改内容时,您实际上不会在画布上添加,删除或更改任何直接。您可以在自己的模型中添加,删除和更改内容,清除画布,然后根据模型重绘画布。

例如,您的模型可能是JS对象,例如

myModel = {
    images: [
        { url: "my/picture.png", position: [123,556] },
        { url: "another/picture.jpg", position: [95,111] }
    ]
}

你会编写函数,1)递增模型中图像位置的值,2)清除画布,3)将模型绘制到画布上。然后,您将创建一个循环(使用requestAnimationFramesetInterval),它将重复执行这三个函数。

对于大型或复杂的项目,我强烈建议使用诸如paperjs之类的画布库,为您实现该流程(这样您就不必考虑创建模型并清除和重绘画布)。它提供了高级功能,例如开箱即用的动画。

答案 1 :(得分:0)

动画基本动作

像所有动画一样,使某些东西看起来像是在移动就是绘制一系列静止图像(一帧),每个图像都略有不同。如果帧的速率足够高(超过每秒20次),人类的眼睛和大脑会将静止图像的序列看作连续的运动。从第一部电影到今天的高端游戏,这就是动画的完成方式。

因此对于画布来说,绘制框架的过程很简单。创建一个清除画布,绘制所需内容的函数,退出该函数,以便浏览器可以将完成的帧移动到显示中。 (请注意,在函数中,在画布上看不到画布上的任何内容,您必须退出该函数以查看绘制的内容)

要制作动画,您必须至少按秒执行上述操作20次以上。为获得最佳效果,您应该以与显示硬件显示帧相同的速率执行此操作。对于总是每秒60帧(fps)的浏览器。

浏览器中的动画

为了帮助您与显示器同步,您可以使用函数requestAnimationFrame(myDrawFunction)它告诉浏览器您正在设置动画,并且只有在显示硬件准备好显示新的完整帧时才会显示渲染结果,而不是绘制函数退出时(可能是硬件框架的中途)。

动画对象

因此,作为一个简单的例子,让我们创建一个动画对象。

const imageSrc = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
const myImage = {
  posX: 0, // current position of object
  posY: 0,
  speed: 3, // speed in pixels per frame (1/60th second)
  direction: 0, // direction of movement in radians 0 is at 3oclock
  image: (() => { // create and load an image (Image will take time to load
    // and may not be ready until after the code has run.
    const image = new Image;
    image.src = imageSrc;
  })(),

绘制功能

然后是在画布上绘制对象的绘图功能

  draw(ctx) {
    ctx.drawImage(this.image, this.posX, this.posY);
  },

更新功能

当我们动画时,我们需要每帧移动一次对象,为此我们创建一个更新函数。

  update() {
    this.posX += (mx = Math.cos(this.direction)) * this.speed;
    this.posY += (my = Math.sin(this.direction)) * this.speed;
  }
} // end of object

多次一秒

要制作动画,我们会创建一个主循环,通过requestAnimationFrame调用每个硬件显示帧。

function mainLoop() {
  // clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // update the obj
  myImage.update();
  // draw the object
  myImage.draw(ctx);
  // request the next frame
  requestAnimationFrame(mainLoop);
  // note all of the above code is not seen by the use until the
  // next hardwares display frame. If you used setTimeout or setInterval
  // then it would be displayed when this function exits (which may be 
  // halfway through a frame resulting in the animation being cut in two)
}
// request the first frame
requestAnimationFrame(mainLoop);

一些额外内容

这是最基本的动画。当然,您需要获取画布上下文并等待图像加载。另外,由于画布的图像移动,您需要检查它何时停止动画。

例如停止动画是图像不在屏幕上

if(myImage.posX < canvas.width){  // only render while image is on the canvas
   requestAnimationFrame(mainLoop);
} else {
   console.log("Animation has ended");
}

现在把它放在一起作为演示。

演示

该演示具有一些额外的智能,可以使图像环绕,确保图像在开始之前加载并使其从屏幕开始,但基本上与上面概述的相同。

// get the 2D context from the canvas id
const ctx = canvas.getContext("2d");
// setup the font and text rendering
ctx.font = "32px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";

// create the image object and load the image
const imageSrc = "https://i.stack.imgur.com/C7qq2.png?s=328&g=1";
const myImage = {
  posX: 0, // current position of object
  posY: 0,
  speed: 3, // speed in pixels per frame (1/60th second)
  direction: 0, // direction of movement in radians 0 is at 3oclock
  image: (() => { // create and load an image (Image will take time to load
    // and may not be ready until after the code has run.
    const image = new Image;
    image.src = imageSrc;
    // to start move the image of the display
    image.onload = function(){
      const imageDiagonalSize = Math.sqrt(
        image.width * image.width + image.height * image.height
      )
      myImage.posX = (canvas.width / 2) - imageDiagonalSize - Math.cos(myImage.direction) * imageDiagonalSize;
      myImage.posX = (canvas.height / 2) - imageDiagonalSize - Math.sin(myImage.direction) * imageDiagonalSize;
    }
    return image;
  })(),
  draw(ctx) {
    ctx.drawImage(this.image, this.posX, this.posY);
  },
  update() {
    var mx,my; // get movement x and y
    this.posX += (mx = Math.cos(this.direction)) * this.speed;
    this.posY += (my = Math.sin(this.direction)) * this.speed;
    // if the image moves of the screen move it to the other side
    if(mx > 0) { // if moving right
      if(this.posX > canvas.width){
         this.posX = 0-this.image.width;
      }
    }else if(mx < 0) { // if moving left
      if(this.posX + this.image.width < 0){
         this.posX = canvas.width;
      }
    }
    if(my > 0) { // if moving down
      if(this.posY > canvas.height){
         this.posY = 0-this.image.height;
      }
    }else if(my < 0) { // if moving up
      if(this.posY + this.image.height < 0){
         this.posY = canvas.height;
      }
    }      
  }
}

function mainLoop() {
  // clear the canvas
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  if(myImage.image.complete) { // wait for image to load
    myImage.update();
    myImage.draw(ctx);
  }else{ // some feedback to say the image is loading
    ctx.fillText("Loading image..",canvas.width / 2, canvas.height / 2);
  }
  // request the next frame
  requestAnimationFrame(mainLoop);
}
// request the first frame
requestAnimationFrame(mainLoop);
canvas {
  border: 2px solid black;
}
<!-- id's must be unique to the page -->
<canvas id="canvas"></canvas>

  • 请注意,上面的代码使用ES6,并且需要像Babel这样的代码预处理器才能在旧版浏览器上运行。