如何正确地对 HTML Canvas 进行下采样以获得好看的图像?

Reg*_*234 7 html javascript css canvas html5-canvas

介绍

我正在尝试处理画布动画上的模糊视觉效果。这种模糊在移动设备、视网膜和高 dpi(每英寸点数)屏幕上尤为普遍。

我正在寻找一种方法来确保使用画布绘制的像素在低 dpi 屏幕和高 dpi 屏幕上看起来最好。作为这个问题的解决方案,我写了多篇关于画布缩小的文章,并遵循了本教程:

https://www.kirupa.com/canvas/canvas_high_dpi_retina.htm

在项目中集成缩减

我想在其中实现缩减的项目可以在下面找到,它包含一些重要的功能:

  • 有一个(大)主画布。(性能优化)
  • 有多个(预渲染的)较小的画布用于绘制和加载图像。(性能优化)
  • 画布是动画的。(在代码片段中,没有可见动画,但集成了动画功能。)

我试图实现的目标:我面临的问题似乎很简单。当网站(带有画布)在移动设备(例如,Iphone,每英寸的像素数高于普通台式机)上打开时。图像显得更加模糊。我真正想要实现的是从图像中消除这种模糊性。我把这个,它说模糊可以通过下采样来消除。我试图在提供的代码中加入这种技术,但它并没有完全奏效。图像变大了,我无法将图像缩放回原始大小。片段它没有正确实现,输出仍然模糊。我做错了什么,我该如何解决这个问题?

代码片段的解释

该变量devicePixelRatio设置为2模拟高 dpi 手机屏幕,低 dpi 屏幕的 adevicePixelRatio为 1。生成的多个预渲染画布的功能spawn是代码片段有 5 个不同的画布,但在生产环境中有 10 个。

如果这篇文章有任何信息缺失或问题,请告诉我。非常感谢!

代码片段

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

//Simulate Retina screen = 2, Normal screen = 1
let devicePixelRatio = 2


function mainCanvasPixelRatio() {
    // get current size of the canvas
    let rect = canvas.getBoundingClientRect();

    // increase the actual size of our canvas
    canvas.width = rect.width * devicePixelRatio;
    canvas.height = rect.height * devicePixelRatio;

    // ensure all drawing operations are scaled
    c.scale(devicePixelRatio, devicePixelRatio);

    // scale everything down using CSS
    canvas.style.width = rect.width + 'px';
    canvas.style.height = rect.height + 'px';
}

// Initial Spawn
function spawn() {
    for (let i = 0; i < 2; i++) {

        //Set Radius
        let radius = parseInt(i*30);

        //Give position
        let x = Math.round((canvas.width/devicePixelRatio) / 2);
        let y = Math.round((canvas.height /devicePixelRatio) / 2);

        //Begin Prerender canvas
        let PreRenderCanvas = document.createElement('canvas');
        const tmp = PreRenderCanvas.getContext("2d");

        //Set PreRenderCanvas width and height
        let PreRenderCanvasWidth = ((radius*2)*1.5)+1;
        let PreRenderCanvasHeight =  ((radius*2)*1.5)+1;


        //Increase the actual size of PreRenderCanvas
        PreRenderCanvas.width = PreRenderCanvasWidth * devicePixelRatio;
        PreRenderCanvas.height = PreRenderCanvasHeight * devicePixelRatio;

        //Scale PreRenderCanvas down using CSS
        PreRenderCanvas.style.width = PreRenderCanvasWidth + 'px';
        PreRenderCanvas.style.height = PreRenderCanvasHeight + 'px';

        //Ensure PreRenderCanvas drawing operations are scaled
        tmp.scale(devicePixelRatio, devicePixelRatio);

        //Init image
        const image=  new Image();

        //Get center of PreRenderCanvas
        let m_canvasCenterX = (PreRenderCanvas.width/devicePixelRatio) * .5;
        let m_canvasCenterY =  (PreRenderCanvas.height/devicePixelRatio) * .5;

        //Draw red circle on PreRenderCanvas
        tmp.strokeStyle = "red";
        tmp.beginPath();
        tmp.arc((m_canvasCenterX), (m_canvasCenterY), ((PreRenderCanvas.width/devicePixelRatio)/3) , 0, 2 * Math.PI);
        tmp.lineWidth = 2;
        tmp.stroke();
        tmp.restore();
        tmp.closePath()

        //Set Image
        image .src=  "https://play-lh.googleusercontent.com/IeNJWoKYx1waOhfWF6TiuSiWBLfqLb18lmZYXSgsH1fvb8v1IYiZr5aYWe0Gxu-pVZX3"

        //Get padding
        let paddingX = (PreRenderCanvas.width/devicePixelRatio)/5;
        let paddingY = (PreRenderCanvas.height/devicePixelRatio)/5;

        //Load image
        image.onload = function () {
            tmp.beginPath()
            tmp.drawImage(image, paddingX,paddingY, (PreRenderCanvas.width/devicePixelRatio)-(paddingX*2),(PreRenderCanvas.height/devicePixelRatio)-(paddingY*2));
            tmp.closePath()
        }

        let circle = new Circle(x, y, c ,PreRenderCanvas);

        circles.push(circle)
    }
}



// Circle parameters
function Circle(x, y, c ,m_canvas) {
    this.x = x;
    this.y = y;
    this.c = c;
    this.m_canvas = m_canvas;
}

//Draw circle on canvas
Circle.prototype = {
    //Draw circle on canvas
    draw: function () {
        this.c.drawImage( this.m_canvas, (this.x - (this.m_canvas.width)/2), (this.y - this.m_canvas.height/2));
    }
};

// Animate
function animate() {
    //Clear canvas each time
    c.clearRect(0, 0, (canvas.width /devicePixelRatio), (canvas.height /devicePixelRatio));

    //Draw in reverse for info overlap
    circles.slice().reverse().forEach(function( circle ) {
        circle.draw();
    });

    requestAnimationFrame(animate);
}

mainCanvasPixelRatio()
spawn()
animate()
Run Code Online (Sandbox Code Playgroud)
#mainCanvas {
     background:blue;
}
Run Code Online (Sandbox Code Playgroud)
<canvas id="mainCanvas"></canvas>

<br>
Run Code Online (Sandbox Code Playgroud)

小智 0

<!DOCTYPE html>
<html>
<body>

<p>Image to use:</p>
<img id="scream" width="220" height="277"
Run Code Online (Sandbox Code Playgroud)

src="pic_the_scream.jpg" alt="尖叫">

<p>Canvas:</p>

<canvas id="myCanvas" width="240" height="297"
Run Code Online (Sandbox Code Playgroud)

样式=“边框:1px实心#d3d3d3;”>

</canvas>

<script>
window.onload = function() {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var img = document.getElementById("scream");
Run Code Online (Sandbox Code Playgroud)

ctx.drawImage(img, 10, 10); };

</script>

</body>
Run Code Online (Sandbox Code Playgroud)