Retina上的画布弧在某些点上太尖锐

时间:2016-09-06 18:30:56

标签: javascript canvas

我正在尝试创建在HiDPI设备上看起来不模糊的动画弧。

这就是我的弧线在iPhone 5s上的样子:

canvas arc

你可以看到0°,90°,180°弧附近变得过于尖锐。我该如何防止这种情况?

这是我的代码:

// Canvas arc progress
const can = document.getElementById('canvas');
const ctx = can.getContext('2d');
const circ = Math.PI * 2;
const quart = Math.PI / 2;
const canvasSize = can.offsetWidth;
const halfCanvasSize = canvasSize / 2;

let start = 0,
  finish = 70,
  animRequestId = null;

// Get pixel ratio
const ratio = (function() {
  const dpr = window.devicePixelRatio || 1,
    bsr = ctx.webkitBackingStorePixelRatio ||
    ctx.mozBackingStorePixelRatio ||
    ctx.msBackingStorePixelRatio ||
    ctx.oBackingStorePixelRatio ||
    ctx.backingStorePixelRatio || 1;

  return dpr / bsr;
})();

// Set canvas h & w
can.width = can.height = canvasSize * ratio;
can.style.width = can.style.height = canvasSize + 'px';
ctx.scale(ratio, ratio)

ctx.beginPath();
ctx.strokeStyle = 'rgb(120,159,194)';
ctx.lineCap = 'square';
ctx.lineWidth = 8.0;
ctx.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, 0, circ, false);
ctx.stroke();
ctx.closePath();

ctx.beginPath();
ctx.strokeStyle = 'rgb(244,247,255)';
ctx.lineCap = 'round';
ctx.lineWidth = 8.0;
ctx.closePath();

let imd = ctx.getImageData(0, 0, canvasSize, canvasSize);

const draw = (current) => {
  ctx.putImageData(imd, 0, 0);
  ctx.beginPath();
  ctx.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, -(quart), ((circ) * current) - quart, false);
  ctx.stroke();
};

(function animateArcProgress() {
  animRequestId = requestAnimationFrame(animateArcProgress);

  if (start <= finish) {
    draw(start / 100);
    start += 2;
  } else {
    cancelAnimationFrame(animRequestId);
  }
})();
body {
  margin: 0;
}
div {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 300px;
  widht: 300px;
  background: #85b1d7;
}
canvas {
  height: 250px;
  width: 250px;
}
<div>
  <canvas id='canvas'></canvas>
</div>

1 个答案:

答案 0 :(得分:2)

您可以通过在下面的代码中绘制1/2像素来柔化边缘。

我在8,7.5和7像素宽度的alpha颜色值分别为0.25,0.5和1渲染了3次。

你可以随心所欲地制作它。

使用putImageData的BTW非常慢,为什么不将背景渲染到另一个画布并通过ctx.drawImage(offScreencanvas,0,0)绘制它,这样你将使用GPU渲染背景而不是通过图形端口总线。

我添加了一些代码,以显示您可以获得的不同柔化效果,并添加鼠标缩放,以便您可以更好地查看像素。

&#13;
&#13;
const can = document.getElementById('canvas');
const can2 = document.createElement("canvas"); // off screen canvas 
const can3 = document.createElement("canvas"); // off screen canvas 

const ctx = can.getContext('2d');
const ctx2 = can2.getContext('2d');
const ctx3 = can3.getContext('2d');
const circ = Math.PI * 2;
const quart = Math.PI / 2;
const canvasSize = can.offsetWidth;
const halfCanvasSize = canvasSize / 2;
const mouse = {x : null, y : null};
can.addEventListener("mousemove",function(e){
    var bounds = can.getBoundingClientRect();
    mouse.x = e.clientX - bounds.left;
    mouse.y = e.clientY - bounds.top;        
});
let start = 0,
  finish = 70,
  animRequestId = null;

// Get pixel ratio
const ratio = (function() {
  const dpr = window.devicePixelRatio || 1,
    bsr = ctx.webkitBackingStorePixelRatio ||
    ctx.mozBackingStorePixelRatio ||
    ctx.msBackingStorePixelRatio ||
    ctx.oBackingStorePixelRatio ||
    ctx.backingStorePixelRatio || 1;

  return dpr / bsr;
})();

// Set canvas h & w
can2.height = can3.height = can2.width = can3.width = can.width = can.height = canvasSize * ratio;
can.style.width = can.style.height = canvasSize + 'px';

ctx.scale(ratio, ratio)
ctx2.scale(ratio, ratio)
ctx3.scale(ratio, ratio)

ctx2.beginPath();
ctx2.strokeStyle = 'rgb(120,159,194)';
ctx2.lineCap = 'square';
ctx2.lineWidth = 8.0;
ctx2.arc(halfCanvasSize, halfCanvasSize, halfCanvasSize - 4, 0, circ, false);
ctx2.stroke();
ctx2.closePath();

ctx2.beginPath();
ctx2.strokeStyle = 'rgb(244,247,255)';
ctx2.lineCap = 'round';
ctx2.lineWidth = 8.0;
ctx2.closePath();


const draw = (current) => {
  ctx3.clearRect(0,0,canvas.width,canvas.height);
  ctx3.drawImage(can2,0,0);
  var rad = halfCanvasSize - 4;
  const drawArc = () => {
      ctx3.beginPath();
      ctx3.arc(halfCanvasSize, halfCanvasSize, rad, -(quart), ((circ) * current) - quart, false);
      ctx3.stroke();          
  }
  // draw soft
  ctx3.strokeStyle = 'rgb(244,247,255)';
  ctx3.lineWidth = 8.5;
  ctx3.globalAlpha = 0.25;
  drawArc();;

  ctx3.lineWidth = 7.0;
  ctx3.globalAlpha  = 0.5;
  drawArc();;

  ctx3.lineWidth = 6.5;
  ctx3.globalAlpha  = 1;
  drawArc();

  // draw normal
  rad -= 12;
  ctx3.lineWidth = 8.0;
  ctx3.globalAlpha = 1;
  drawArc();;


  
  // draw ultra soft
  rad -= 12;
  ctx3.strokeStyle = 'rgb(244,247,255)';
  ctx3.lineWidth = 9.0;
  ctx3.globalAlpha = 0.1;
  drawArc();

  ctx3.lineWidth = 8.0;
  ctx3.globalAlpha  = 0.2;
  drawArc();;

  ctx3.lineWidth = 7.5;
  ctx3.globalAlpha  = 0.5;
  drawArc();

  ctx3.lineWidth = 6;
  ctx3.globalAlpha  = 1;
  drawArc();




};

const zoomW = 30;
const zoomAmount = 5;
const drawZoom = () => {
    ctx.drawImage(can3,0,0);
    var width = zoomW * zoomAmount;
    var cx = mouse.x -  width / 2;
    var cy = mouse.y -  width / 2;
    var c1x = mouse.x -  zoomW / 2;
    var c1y = mouse.y -  zoomW / 2;
    ctx.strokeStyle = 'rgb(244,247,255)';
    ctx.lineWidth = 4;
    ctx.strokeRect(cx,cy,width,width);
    ctx.clearRect(cx,cy,width,width);
    ctx.imageSmoothingEnabled = false;
    ctx.mozImageSmoothingEnabled = false;
    ctx.drawImage(can3,c1x,c1y,zoomW,zoomW,cx,cy,width,width);
    ctx.imageSmoothingEnabled = true;
    ctx.mozImageSmoothingEnabled = true;
}
function keepUpdating(){
    ctx.clearRect(0,0,can.width,can.height);
    drawZoom();
    requestAnimationFrame(keepUpdating);
}

(function animateArcProgress() {
  ctx.clearRect(0,0,can.width,can.height);

  draw(start / 100);
  drawZoom();
  
  
  if (start <= finish) {
    start += 0.5;
    requestAnimationFrame(animateArcProgress);
  } else {
    requestAnimationFrame(keepUpdating);  
  }
})();
&#13;
body {
  margin: 0;
}
div {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 300px;
  widht: 300px;
  background: #85b1d7;
}
canvas {
  height: 250px;
  width: 250px;
}
&#13;
<div>
  <canvas id='canvas'></canvas>
</div>
&#13;
&#13;
&#13;