【问题标题】:change shapre of img改变图像的形状
【发布时间】:2020-01-15 08:48:59
【问题描述】:

我需要帮助将移动方形图像转换为 html 画布中的圆圈

我导入了一个正方形的图像。我希望该图像是圆形的并且仍然可以移动。

目标:

  • 方形图像到圆形图像,仍然可以移动。

我已经尝试了很多关于stackoverflow的教程,主要是用c。 stroke 和 c.split 但是当我应用它们时,图像不再移动。

有人有什么建议吗?

var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

function Circle() {
    //Give var for circle
    this.x = 10;
    this.y = 100;
    this.dx = 1;
    this.dy = 1;
    this.radius = 50;
    this.diameter = 2 * this.radius;

    //Get external square picture (Needs to be converted in circle)
    var image = new Image();
    image.src = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";

    //Draw circle on canvas
    this.draw = function () {
        //Circle
        c.beginPath();
        c.arc(this.x, this.y, (this.radius*1), 0, Math.PI * 2, false);
        c.closePath();

        //TODO: cut square to circle

        //Place square (image) on top of the circle
        c.drawImage(image, (this.x-this.diameter/2) , (this.y-this.diameter/2), this.diameter, this.diameter);
    };

    //Update position
    this.update = function () {
        this.x += this.dx;
        this.draw()
    }
}

//Animate canvas
function animate() {
    requestAnimationFrame(animate);
    c.clearRect(0, 0, innerWidth, innerHeight);
    this.update();
}

//Start
Circle();
animate();
canvas {
    border: 1px solid black;
    background: black;

}
<!DOCTYPE html>
<html>
<head>
</head>
<body>

<canvas></canvas>

</body>
</html>

【问题讨论】:

    标签: javascript html css canvas html5-canvas


    【解决方案1】:

    答案是在您的情况下使用context.clip(); c.clip(); 这会在画布上创建一个剪辑过滤器,然后您可以在其中进行绘制。在制作剪辑之前,您必须进行保存,然后在使用 @ 绘制后恢复分别为 987654323@ 和 c.restore()

    var canvas = document.querySelector('canvas');
    var c = canvas.getContext('2d');
    var circles = [];
    
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    function Circle() {
        //Give var for circle
        this.x = 100;
        this.y = 100;
        this.dx = 1;
        this.dy = 1;
        this.radius = 50;
        this.diameter = 2 * this.radius;
        this.size = null;
        this.c = null;
    
        //Get external square picture (Needs to be converted in circle)
        var image = new Image();
        image.src = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
    
        //Draw circle on canvas
        this.draw = function () {
            //Circle
            this.c.beginPath();
            this.c.arc(this.x, this.y, (this.radius*1), 0, Math.PI * 2, false);
            this.c.closePath();
            this.c.save();
            this.c.clip();
            //TODO: cut square to circle
    
            //Place square (image) on top of the circle
            this.c.drawImage(image, (this.x-this.diameter/2) , (this.y-this.diameter/2), this.diameter, this.diameter);
            this.c.restore();
        };
    
        //Update position
        this.update = function () {
            if(this.x - this.radius <= 0 || this.x + this.radius >= this.size.x){this.dx = -this.dx}
            
            this.x += this.dx;
            this.draw()
        }
    
        this.init = function(options) {
          
           Object.keys(options).forEach((key)=>{
              this[key]=options[key];
           })
        }
    }
    
    //Animate canvas
    function animate() {
        requestAnimationFrame(animate);
        c.clearRect(0, 0, innerWidth, innerHeight);
        for(let i = 0; i < circles.length; i++){ circles[i].update(); }
    }
    
    //Start
    for(let i = 0; i < 100; i-=-1){
        let circle = new Circle();
        circle.init({
            x: Math.random() * window.innerWidth,
            y: Math.random() * window.innerHeight,
            size: {x: window.innerWidth,y: window.innerHeight},
            c
        })
        circles.push(circle)
    }
    animate();
    canvas {
        border: 1px solid black;
        background: black;
    
    }
    body {
      margin: 0;
      padding: 0;
      overflow-x: hidden;
    }
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    
    <canvas></canvas>
    
    </body>
    </html>

    【讨论】:

    • 非常感谢它的帮助!我现在唯一的问题是,当我以更高的移动速度生成更多的圆圈时,它会疯狂地滞后,使用这种实现(通常不会)。您有解决方法或其他优化技巧吗?
    • @AttackTheWar 我对您的代码进行了一些编辑,以允许实例化对象并允许它们来回弹跳,但正如您所见,甚至 20 个来回弹跳的对象都可以正常工作。我认为这可能是您实例化它们的方式。
    • 再次感谢!我在画布中生成 100 个对象。如果没有方形到圆形的转换,一切都会顺利进行。使用您提供给我的代码时。它像疯了一样落后。所以可能我必须在 this.draw 函数之前对图像做一些事情。在 Circle 函数中的某个地方。对此有何建议?
    • 你是如何实例化 Circle 函数的?在上面的代码中,我已经创建了 100 个圆圈并且运行顺利
    【解决方案2】:

    首先生成您的合成图像,然后在您的动画代码中使用它。

    要进行这样的合成,最好使用合成而不是剪裁,因为剪裁是全有或全无的操作,它不能很好地处理圆形所需的抗锯齿。

    要以方便的方式存储此合成图像,您可以使用第二个画布,或者在最新的浏览器中使用ImageBitmap,然后两者都可以由drawImage 绘制。

    这是一个 ES5 实现,使用第二个画布和一个回调:

    // older browsers version
    function makeCircleImage(radius, src, success, failure) {
      var canvas = document.createElement('canvas');
      canvas.width = canvas.height = radius * 2;
      var ctx = canvas.getContext("2d");
      var img = new Image();
      img.src = src;
      img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        // we use compositing, offers better antialiasing than clip()
        ctx.globalCompositeOperation = 'destination-in';
        ctx.arc(radius, radius, radius, 0, Math.PI*2);
        ctx.fill();
        success(canvas);
      };
      img.onerror = failure;
    }
    var url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
    makeCircleImage( 50, url, function( canvas ) {
      document.body.appendChild(canvas);
    }, console.error );
    canvas {
      border: 1px solid black;
      background: black;
    }

    这是一个你可以在最新的浏览器中使用的一个,它将存储一个 ImageBitmap(当内存和 理论 绘图速度可用时最好)。

    // newest browsers version
    function makeCircleImage( radius, src ) {
      return new Promise( (resolve, reject) => {
        // this canvas will get Garbage Collected
        const canvas = document.createElement( 'canvas' );
        canvas.width = canvas.height = radius * 2;
        const ctx = canvas.getContext( "2d" );
        const img = new Image();
        img.src = src;
        img.onload = function() {
          ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
          // we use compositing, offers better antialiasing than clip()
          ctx.globalCompositeOperation = 'destination-in';
          ctx.arc( radius, radius, radius, 0, Math.PI*2 );
          ctx.fill();
          resolve( createImageBitmap( canvas ) );
        };
        img.onerror = reject;
      });
    }
    
    async function init() {
      const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
      const img = await makeCircleImage(50, url);
      document.querySelector('canvas')
        .getContext('2d')
        .drawImage(img, 0,0);
    };
    init().catch( console.error );
    canvas {
      border: 1px solid black;
      background: black;
    }
    &lt;canvas&gt;&lt;/canvas&gt;

    现在我们有一个清晰的源代码可供使用,我们在动画代码中要做的就是放置这些像素,无需任何繁重的计算:

    // older browsers version
    function makeCircleImage(radius, src, callback) {
      var canvas = document.createElement('canvas');
      canvas.width = canvas.height = radius * 2;
      var ctx = canvas.getContext("2d");
      var img = new Image();
      img.src = src;
      img.onload = function() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        // we use compositing, offers better antialiasing than clip()
        ctx.globalCompositeOperation = 'destination-in';
        ctx.arc(radius, radius, radius, 0, Math.PI*2);
        ctx.fill();
        callback(canvas);
      };
    }
    
    function Circle( x, y, radius ) {
      //Give var for circle
      this.x = x;
      this.y = y;
      this.dx = 1;
      this.dy = 1;
      this.radius = radius;
    }
    // use prototyping if you wish to make it a class
    Circle.prototype = {
    //Draw circle on canvas
      draw: function () {
        var
          x = (this.x - this.radius),
          y = (this.y - this.radius);
        // draw is a single call
        c.drawImage( this.image, x, y );
      },
      update: function () {
        var
          max_right = canvas.width + this.radius,
          max_left = this.radius * -1;
        this.x += this.dx;
        if( this.x > max_right ) {
          this.x += max_right - this.x;
          this.dx *= -1;
        }
        if( this.x < max_left ) {
          this.x += max_left - this.x;
          this.dx *= -1;
        }
      },
      init: function(callback) {
        var url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
        makeCircleImage( this.radius, url, function(img) {
          this.image = img;
          callback();
        }.bind(this));
      }
    };
    
    //Animate canvas
    function animate() {
      c.clearRect(0, 0, window.innerWidth, window.innerHeight);
      circles.forEach(function( circle ) {
        circle.update();
      });
      circles.forEach(function( circle ) {
        circle.draw();
      });
      requestAnimationFrame(animate);
    }
    
    var canvas = document.querySelector('canvas');
    var c = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    var circles = [
      new Circle(10, 100, 50),
      new Circle(10, 200, 30),
      new Circle(10, 300, 50)
    ];
    var ready = 0;
    circles.forEach(function(circle) {
      circle.init(oncircledone);
    });
    function oncircledone() {
      if(++ready === circles.length) {
        animate()
      }
    }
    canvas {
        border: 1px solid black;
        background: black;
    
    }
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    
    <canvas></canvas>
    
    </body>
    </html>

    对于最新的浏览器:

    // newest browsers version
    function makeCircleImage( radius, src ) {
      return new Promise( (resolve, reject) => {
        // this canvas will get Garbage Collected
        const canvas = document.createElement( 'canvas' );
        canvas.width = canvas.height = radius * 2;
        const ctx = canvas.getContext( "2d" );
        const img = new Image();
        img.src = src;
        img.onload = function() {
          ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
          // we use compositing, offers better antialiasing than clip()
          ctx.globalCompositeOperation = 'destination-in';
          ctx.arc( radius, radius, radius, 0, Math.PI*2 );
          ctx.fill();
          resolve( createImageBitmap( canvas ) );
        };
        img.onerror = reject;
      });
    }
    
    
    class Circle {
      constructor( x, y, radius ) {
        this.x = x;
        this.y = y;
        this.dx = 1;
        this.dy = 1;
        this.radius = radius;
      }
      draw() {
        const x = (this.x - this.radius);
        const y = (this.y - this.radius);
        c.drawImage( this.image, x, y );
      }
      update() {
        const max_right = canvas.width + this.radius;
        const max_left = this.radius * -1;
        this.x += this.dx;
        if( this.x > max_right ) {
          this.x += max_right - this.x;
          this.dx *= -1;
        }
        if( this.x < max_left ) {
          this.x += max_left - this.x;
          this.dx *= -1;
        }
      }
      async init() {
        const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
        this.image = await makeCircleImage( this.radius, url );
      }
    }
    
    //Animate canvas
    function animate() {
      c.clearRect(0, 0, window.innerWidth, window.innerHeight);
      circles.forEach( (circle) => circle.update() );
      circles.forEach( (circle) => circle.draw() );
    
      requestAnimationFrame(animate);
    }
    
    const canvas = document.querySelector('canvas');
    const c = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    const circles = [
      new Circle(10, 50, 50),
      new Circle(10, 150, 30),
      new Circle(450, 250, 80)
    ];
    
    Promise.all(circles.map( (circle) => circle.init() ))
      .then(animate)
      .catch(console.error);
    canvas {
        border: 1px solid black;
        background: black;
    
    }
    &lt;canvas&gt;&lt;/canvas&gt;

    但是,如果您将拥有大量 Circle 实例,并且它们的半径不同,但共享相同的原始图像源,那么您可能更愿意存储合成图像的单个高质量版本,并且只在每个实例的绘图调用中重新缩放它。

    // newest browsers version
    function makeCircleImage( radius, src ) {
      return new Promise( (resolve, reject) => {
        // this canvas will get Garbage Collected
        const canvas = document.createElement( 'canvas' );
        canvas.width = canvas.height = radius * 2;
        const ctx = canvas.getContext( "2d" );
        const img = new Image();
        img.src = src;
        img.onload = function() {
          ctx.drawImage( img, 0, 0, canvas.width, canvas.height );
          // we use compositing, offers better antialiasing than clip()
          ctx.globalCompositeOperation = 'destination-in';
          ctx.arc( radius, radius, radius, 0, Math.PI*2 );
          ctx.fill();
          resolve( createImageBitmap( canvas ) );
        };
        img.onerror = reject;
      });
    }
    
    (async () => {
    const url = "https://assets.coingecko.com/coins/images/2607/large/molecular_future.png?1547036754";
    const image = await makeCircleImage(250, url); // original size of the png image
    class Circle {
      constructor( x, y, radius ) {
        this.x = x;
        this.y = y;
        this.dx = 1;
        this.dy = 1;
        this.radius = radius;
      }
      draw() {
        const x = (this.x - this.radius);
        const y = (this.y - this.radius);
        // now they all use the same image
        // with a bit more computation though due to resizing
        c.drawImage( image, x, y , this.radius * 2, this.radius * 2);
      }
      update() {
        const max_right = canvas.width + this.radius;
        const max_left = this.radius * -1;
        this.x += this.dx;
        if( this.x > max_right ) {
          this.x += max_right - this.x;
          this.dx *= -1;
        }
        if( this.x < max_left ) {
          this.x += max_left - this.x;
          this.dx *= -1;
        }
      }
    }
    
    //Animate canvas
    function animate() {
      c.clearRect(0, 0, window.innerWidth, window.innerHeight);
      circles.forEach( (circle) => circle.update() );
      circles.forEach( (circle) => circle.draw() );
    
      requestAnimationFrame(animate);
    }
    
    const canvas = document.querySelector('canvas');
    const c = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    const rand = (n) => Math.random() * n;
    const circles = Array.from({ length: 50 })
      .map( () =>
        new Circle(
          rand(canvas.width),
          rand(canvas.height),
          rand(125)
        )
      );
    
    animate();
    
    })()
    .catch(console.error);
    canvas {
        border: 1px solid black;
        background: black;
    
    }
    &lt;canvas&gt;&lt;/canvas&gt;

    【讨论】:

      猜你喜欢
      • 2014-06-27
      • 2021-10-12
      • 2012-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多