[javascript] HTML5 캔버스에서 흐릿한 텍스트를 수정하려면 어떻게해야합니까?

나는 총 n00b 이며 모양, 색상 및 텍스트를 렌더링 HTML5하기 canvas위해 작업하고 있습니다. 내 앱 에는 캔버스를 동적으로 만들고 콘텐츠로 채우는 뷰 어댑터 가 있습니다. 내 텍스트가 매우 흐릿하거나 흐릿하게 렌더링된다는 점을 제외하면 정말 잘 작동합니다. 너비높이 를 정의 CSS하면이 문제가 발생 하는 이유에 대한 다른 게시물을 많이 보았지만 모두 javascript.

관련 코드 ( Fiddle 보기 ) :

var width  = 500;//FIXME:size.w;
var height = 500;//FIXME:size.h;

var canvas = document.createElement("canvas");
//canvas.className="singleUserCanvas";
canvas.width=width;
canvas.height=height;
canvas.border = "3px solid #999999";
canvas.bgcolor = "#999999";
canvas.margin = "(0, 2%, 0, 2%)";

var context = canvas.getContext("2d");

//////////////////
////  SHAPES  ////
//////////////////

var left = 0;

//draw zone 1 rect
context.fillStyle = "#8bacbe";
context.fillRect(0, (canvas.height*5/6)+1, canvas.width*1.5/8.5, canvas.height*1/6);

left = left + canvas.width*1.5/8.5;

//draw zone 2 rect
context.fillStyle = "#ffe381";
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*2.75/8.5, canvas.height*1/6);

left = left + canvas.width*2.75/8.5 + 1;

//draw zone 3 rect
context.fillStyle = "#fbbd36";
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*1.25/8.5, canvas.height*1/6);

left = left + canvas.width*1.25/8.5;

//draw target zone rect
context.fillStyle = "#004880";
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*0.25/8.5, canvas.height*1/6);

left = left + canvas.width*0.25/8.5;

//draw zone 4 rect
context.fillStyle = "#f8961d";
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width*1.25/8.5, canvas.height*1/6);

left = left + canvas.width*1.25/8.5 + 1;

//draw zone 5 rect
context.fillStyle = "#8a1002";
context.fillRect(left+1, (canvas.height*5/6)+1, canvas.width-left, canvas.height*1/6);

////////////////
////  TEXT  ////
////////////////

//user name
context.fillStyle = "black";
context.font = "bold 18px sans-serif";
context.textAlign = 'right';
context.fillText("User Name", canvas.width, canvas.height*.05);

//AT:
context.font = "bold 12px sans-serif";
context.fillText("AT: 140", canvas.width, canvas.height*.1);

//AB:
context.fillText("AB: 94", canvas.width, canvas.height*.15);

//this part is done after the callback from the view adapter, but is relevant here to add the view back into the layout.
var parent = document.getElementById("layout-content");
parent.appendChild(canvas);
<div id="layout-content"></div>

내가보고있는 결과 ( Safari에서 )는 Fiddle에 표시된 것보다 훨씬 더 왜곡되어 있습니다.

나의 것

Safari에서 렌더링 된 출력

깡깡이

JSFiddle에서 렌더링 된 출력

내가 뭘 잘못하고 있니? 각 텍스트 요소에 대해 별도의 캔버스가 필요합니까? 글꼴인가요? 먼저 HTML5 레이아웃에서 캔버스를 정의해야합니까? 오타가 있습니까? 나는 길을 잃었다.



답변

캔버스 요소는 장치 또는 모니터의 픽셀 비율과 독립적으로 실행됩니다.

iPad 3+에서이 비율은 2입니다. 이것은 기본적으로 1000px 너비 캔버스가 이제 iPad 디스플레이에 명시된 너비와 일치하도록 2000px를 채워야 함을 의미합니다. 다행히도이 작업은 브라우저에서 자동으로 수행됩니다. 다른 한편으로, 이것이 보이는 영역에 직접적으로 맞도록 만들어진 이미지와 캔버스 요소에 대한 정의가 덜 보이는 이유이기도합니다. 캔버스는 1000px를 채우는 방법 만 알고 있지만 2000px로 그려야하기 때문에 브라우저는 이제 요소를 적절한 크기로 표시하기 위해 픽셀 사이의 공백을 지능적으로 채워야합니다.

내가보기 엔 당신이 읽고 추천 이 문서 에서 HTML5ROCKS을 고화질 요소를 작성하는 방법을 더 자세히 설명합니다.

tl; dr? 다음은 적절한 해상도로 캔버스를 뱉어 내기 위해 내 프로젝트에서 사용하는 예제 (위의 tut 기반)입니다.

var PIXEL_RATIO = (function () {
    var ctx = document.createElement("canvas").getContext("2d"),
        dpr = window.devicePixelRatio || 1,
        bsr = ctx.webkitBackingStorePixelRatio ||
              ctx.mozBackingStorePixelRatio ||
              ctx.msBackingStorePixelRatio ||
              ctx.oBackingStorePixelRatio ||
              ctx.backingStorePixelRatio || 1;

    return dpr / bsr;
})();


createHiDPICanvas = function(w, h, ratio) {
    if (!ratio) { ratio = PIXEL_RATIO; }
    var can = document.createElement("canvas");
    can.width = w * ratio;
    can.height = h * ratio;
    can.style.width = w + "px";
    can.style.height = h + "px";
    can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
    return can;
}

//Create canvas with the device resolution.
var myCanvas = createHiDPICanvas(500, 250);

//Create canvas with a custom resolution.
var myCustomCanvas = createHiDPICanvas(500, 200, 4);

도움이 되었기를 바랍니다!


답변

해결되었습니다!

캔버스 크기에 어떤 영향을 미치는지 확인하기 위해 설정 한 너비높이 속성을 변경하는 것이 무엇인지보기로 결정 javascript했지만 그렇지 않았습니다. 해상도를 변경합니다.

원하는 결과를 얻으려면 canvas.style.width속성 을 설정해야 했으며이 속성은의 물리적 크기를 변경해야합니다 canvas.

canvas.width=1000;//horizontal resolution (?) - increase for better looking text
canvas.height=500;//vertical resolution (?) - increase for better looking text
canvas.style.width=width;//actual width of canvas
canvas.style.height=height;//actual height of canvas


답변

CSS를 통해 캔버스 요소의 크기를 조정하여 부모 요소의 전체 너비를 가져옵니다. 내 요소의 너비와 높이가 조정되지 않는다는 것을 알았습니다. 크기를 설정하는 가장 좋은 방법을 찾고있었습니다.

canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;

이 간단한 방법은 어떤 화면을 사용하든 상관없이 캔버스를 완벽하게 설정합니다.


답변

이 100 %가 나를 위해 해결했습니다.

var canvas = document.getElementById('canvas');
canvas.width = canvas.getBoundingClientRect().width;
canvas.height = canvas.getBoundingClientRect().height;

(Adam Mańkowski의 솔루션에 가깝습니다).


답변

canvg (SVG to Canvas js 라이브러리) 에서 MyNameIsKo 코드를 약간 수정했습니다 . 나는 잠시 혼란 스러웠고 이것을 위해 시간을 보냈다. 이것이 누군가를 돕기를 바랍니다.

HTML

<div id="chart"><canvas></canvas><svg>Your SVG here</svg></div>

자바 스크립트

window.onload = function() {

var PIXEL_RATIO = (function () {
    var ctx = document.createElement("canvas").getContext("2d"),
        dpr = window.devicePixelRatio || 1,
        bsr = ctx.webkitBackingStorePixelRatio ||
              ctx.mozBackingStorePixelRatio ||
              ctx.msBackingStorePixelRatio ||
              ctx.oBackingStorePixelRatio ||
              ctx.backingStorePixelRatio || 1;

    return dpr / bsr;
})();

setHiDPICanvas = function(canvas, w, h, ratio) {
    if (!ratio) { ratio = PIXEL_RATIO; }
    var can = canvas;
    can.width = w * ratio;
    can.height = h * ratio;
    can.style.width = w + "px";
    can.style.height = h + "px";
    can.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
}

var svg = document.querySelector('#chart svg'),
    canvas = document.querySelector('#chart canvas');

var svgSize = svg.getBoundingClientRect();
var w = svgSize.width, h = svgSize.height;
setHiDPICanvas(canvas, w, h);

var svgString = (new XMLSerializer).serializeToString(svg);
var ctx = canvas.getContext('2d');
ctx.drawSvg(svgString, 0, 0, w, h);

}


답변

다른 답변에서 언급되지 않은 세부 사항을 발견했습니다. 캔버스 해상도는 정수 값으로 잘립니다.

기본 캔버스 해상도 치수는 다음 canvas.width: 300과 같습니다.canvas.height: 150 입니다.

내 화면에서 window.devicePixelRatio: 1.75 .

그래서 내가 설정할 canvas.height = 1.75 * 150때 값은 원하는에서 262.5아래로 잘립니다.262 .

해결책은 주어진 CSS 레이아웃 크기를 선택하는 것입니다. window.devicePixelRatio 해상도를 조정할 때 잘림이 발생하지 않도록 하는 것입니다.

예를 들어,를 곱할 때 정수를 생성 하는 width: 300pxand height: 152px를 사용할 수 있습니다 1.75.

편집 : 또 다른 해결책은 CSS 픽셀이 소수 일 수 있다는 사실을 활용하여 캔버스 픽셀 크기 조정의 잘림을 방지하는 것입니다.

아래는이 전략을 사용한 데모입니다.

편집 : 다음은이 전략을 사용하도록 업데이트 된 OP의 바이올린입니다 : http://jsfiddle.net/65maD/83/ .

main();

// Rerun on window resize.
window.addEventListener('resize', main);


function main() {
  // Prepare canvas with properly scaled dimensions.
  scaleCanvas();

  // Test scaling calculations by rendering some text.
  testRender();
}


function scaleCanvas() {
  const container = document.querySelector('#container');
  const canvas = document.querySelector('#canvas');

  // Get desired dimensions for canvas from container.
  let {width, height} = container.getBoundingClientRect();

  // Get pixel ratio.
  const dpr = window.devicePixelRatio;

  // (Optional) Report the dpr.
  document.querySelector('#dpr').innerHTML = dpr.toFixed(4);

  // Size the canvas a bit bigger than desired.
  // Use exaggeration = 0 in real code.
  const exaggeration = 20;
  width = Math.ceil (width * dpr + exaggeration);
  height = Math.ceil (height * dpr + exaggeration);

  // Set the canvas resolution dimensions (integer values).
  canvas.width = width;
  canvas.height = height;

  /*-----------------------------------------------------------
                         - KEY STEP -
   Set the canvas layout dimensions with respect to the canvas
   resolution dimensions. (Not necessarily integer values!)
   -----------------------------------------------------------*/
  canvas.style.width = `${width / dpr}px`;
  canvas.style.height = `${height / dpr}px`;

  // Adjust canvas coordinates to use CSS pixel coordinates.
  const ctx = canvas.getContext('2d');
  ctx.scale(dpr, dpr);
}


function testRender() {
  const canvas = document.querySelector('#canvas');
  const ctx = canvas.getContext('2d');

  // fontBaseline is the location of the baseline of the serif font
  // written as a fraction of line-height and calculated from the top
  // of the line downwards. (Measured by trial and error.)
  const fontBaseline = 0.83;

  // Start at the top of the box.
  let baseline = 0;

  // 50px font text
  ctx.font = `50px serif`;
  ctx.fillText("Hello World", 0, baseline + fontBaseline * 50);
  baseline += 50;

  // 25px font text
  ctx.font = `25px serif`;
  ctx.fillText("Hello World", 0, baseline + fontBaseline * 25);
  baseline += 25;

  // 12.5px font text
  ctx.font = `12.5px serif`;
  ctx.fillText("Hello World", 0, baseline + fontBaseline * 12.5);
}
/* HTML is red */

#container
{
  background-color: red;
  position: relative;
  /* Setting a border will mess up scaling calculations. */

  /* Hide canvas overflow (if any) in real code. */
  /* overflow: hidden; */
}

/* Canvas is green */

#canvas
{
  background-color: rgba(0,255,0,.8);
  animation: 2s ease-in-out infinite alternate both comparison;
}

/* animate to compare HTML and Canvas renderings */

@keyframes comparison
{
  33% {opacity:1; transform: translate(0,0);}
  100% {opacity:.7; transform: translate(7.5%,15%);}
}

/* hover to pause */

#canvas:hover, #container:hover > #canvas
{
  animation-play-state: paused;
}

/* click to translate Canvas by (1px, 1px) */

#canvas:active
{
  transform: translate(1px,1px) !important;
  animation: none;
}

/* HTML text */

.text
{
  position: absolute;
  color: white;
}

.text:nth-child(1)
{
  top: 0px;
  font-size: 50px;
  line-height: 50px;
}

.text:nth-child(2)
{
  top: 50px;
  font-size: 25px;
  line-height: 25px;
}

.text:nth-child(3)
{
  top: 75px;
  font-size: 12.5px;
  line-height: 12.5px;
}
<!-- Make the desired dimensions strange to guarantee truncation. -->
<div id="container" style="width: 313.235px; height: 157.122px">
  <!-- Render text in HTML. -->
  <div class="text">Hello World</div>
  <div class="text">Hello World</div>
  <div class="text">Hello World</div>

  <!-- Render text in Canvas. -->
  <canvas id="canvas"></canvas>
</div>

<!-- Interaction instructions. -->
<p>Hover to pause the animation.<br>
Click to translate the green box by (1px, 1px).</p>

<!-- Color key. -->
<p><em style="color:red">red</em> = HTML rendered<br>
<em style="color:green">green</em> = Canvas rendered</p>

<!-- Report pixel ratio. -->
<p>Device pixel ratio: <code id="dpr"></code>
<em>(physical pixels per CSS pixel)</em></p>

<!-- Info. -->
<p>Zoom your browser to re-run the scaling calculations.
(<code>Ctrl+</code> or <code>Ctrl-</code>)</p>


답변

reactjs에서 일하는 사람들을 위해 MyNameIsKo의 답변을 수정했으며 훌륭하게 작동합니다. 다음은 코드입니다.

import React from 'react'

export default class CanvasComponent extends React.Component {
    constructor(props) {
        this.calcRatio = this.calcRatio.bind(this);
    }

    // Use componentDidMount to draw on the canvas
    componentDidMount() {
        this.updateChart();
    }

    calcRatio() {
        let ctx = document.createElement("canvas").getContext("2d"),
        dpr = window.devicePixelRatio || 1,
        bsr = ctx.webkitBackingStorePixelRatio ||
          ctx.mozBackingStorePixelRatio ||
          ctx.msBackingStorePixelRatio ||
          ctx.oBackingStorePixelRatio ||
          ctx.backingStorePixelRatio || 1;
        return dpr / bsr;
    }

    // Draw on the canvas
    updateChart() {

        // Adjust resolution
        const ratio = this.calcRatio();
        this.canvas.width = this.props.width * ratio;
        this.canvas.height = this.props.height * ratio;
        this.canvas.style.width = this.props.width + "px";
        this.canvas.style.height = this.props.height + "px";
        this.canvas.getContext("2d").setTransform(ratio, 0, 0, ratio, 0, 0);
        const ctx = this.canvas.getContext('2d');

       // now use ctx to draw on the canvas
    }


    render() {
        return (
            <canvas ref={el=>this.canvas=el} width={this.props.width} height {this.props.height}/>
        )
    }
}

이 예에서는 캔버스의 너비와 높이를 소품으로 전달합니다.