Bagaimana cara memperbaiki teks buram di kanvas HTML5 saya?

89

Saya total n00b dengan HTML5dan saya bekerja dengan canvasuntuk membuat bentuk, warna, dan teks. Di aplikasi saya, saya memiliki adaptor tampilan yang membuat kanvas secara dinamis, dan mengisinya dengan konten. Ini bekerja dengan sangat baik, kecuali teks saya dibuat sangat kabur / buram / membentang. Saya telah melihat banyak posting lain tentang mengapa mendefinisikan lebar dan tinggi di CSSakan menyebabkan masalah ini, tapi saya mendefinisikan semuanya dalam javascript.

Kode yang relevan (lihat Biola ):

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>

Hasil yang saya lihat (di Safari ) jauh lebih miring daripada yang ditunjukkan di Fiddle:

Milikku

Output yang diberikan di Safari

Biola

Menampilkan keluaran di JSFiddle

Apa yang saya lakukan dengan tidak benar? Apakah saya memerlukan kanvas terpisah untuk setiap elemen teks? Apa itu fontnya? Apakah saya harus terlebih dahulu menentukan kanvas dalam tata letak HTML5? Apakah ada kesalahan ketik? Saya tersesat.

Phil
sumber
Sepertinya Anda tidak menelepon clearRect.
David
1
Polyfill ini memperbaiki operasi kanvas paling dasar dengan browser HiDPI yang tidak secara otomatis meningkatkan (saat ini hanya safari) ... github.com/jondavidjohn/hidpi-canvas-polyfill
jondavidjohn
Saya telah mengembangkan kerangka JS yang memecahkan masalah buram kanvas dengan mosaik DIV. Saya menghasilkan gambar yang lebih jelas dan lebih tajam dengan biaya tertentu dalam hal mem / cpu js2dx.com
Gonki

Jawaban:

160

Elemen kanvas berjalan independen dari rasio piksel perangkat atau monitor.

Pada iPad 3+, rasionya adalah 2. Ini pada dasarnya berarti kanvas dengan lebar 1000px Anda sekarang harus mengisi 2000px agar sesuai dengan lebar yang dinyatakan pada layar iPad. Untungnya bagi kami, ini dilakukan secara otomatis oleh browser. Di sisi lain, ini juga alasan mengapa Anda melihat lebih sedikit definisi pada gambar dan elemen kanvas yang dibuat agar sesuai secara langsung dengan area yang terlihat. Karena kanvas Anda hanya tahu cara mengisi 1000px tetapi diminta untuk menggambar ke 2000px, browser sekarang harus dengan cerdas mengisi kekosongan di antara piksel untuk menampilkan elemen pada ukuran yang sesuai.

Saya akan sangat menyarankan Anda membaca artikel ini dari HTML5Rocks yang menjelaskan secara lebih rinci cara membuat elemen definisi tinggi.

tl; dr? Berikut adalah contoh (berdasarkan tut di atas) yang saya gunakan dalam proyek saya sendiri untuk mengeluarkan kanvas dengan resolusi yang tepat:

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);

Semoga ini membantu!

MyNameIsKo
sumber
2
Pikir itu akan layak disebutkan bahwa metode createHiDPI () saya agak buruk nama. DPI adalah istilah untuk hanya cetak dan PPI adalah singkatan yang lebih tepat karena monitor menampilkan gambar menggunakan piksel sebagai lawan titik.
MyNameIsKo
3
Perhatikan bahwa rasio ini sebenarnya dapat berubah selama masa pakai halaman. Misalnya, jika saya menyeret jendela Chrome dari monitor eksternal res "standar" yang lebih lama ke layar retina bawaan dari macbook, kode akan menghitung rasio yang berbeda. Sekadar informasi jika Anda berencana untuk menyimpan nilai ini ke cache. (eksternal adalah rasio 1, layar retina 2 jika Anda penasaran)
Aardvark
1
Terima kasih atas penjelasannya. Tapi bagaimana dengan aset gambar? Apakah kita perlu menyediakan setiap gambar kanvas dengan resolusi ganda dan menurunkannya secara manual?
Kokodoko
1
Lebih banyak trivia: IE Windows Phone 8 selalu melaporkan 1 untuk window.devicePixelRatio (dan panggilan piksel pendukung tidak berfungsi). Terlihat buruk pada 1, namun rasio 2 terlihat bagus. Untuk saat ini perhitungan rasio saya selalu mengembalikan setidaknya 2 (solusi yang buruk, tetapi platform target saya adalah ponsel modern yang hampir semuanya memiliki layar DPI tinggi). Diuji pada HTC 8X dan Lumia 1020.
Aardvark
1
@Aardvark: tampaknya juga msBackingStorePixelRatio selalu tidak ditentukan. Mengajukan pertanyaan baru tentang itu di sini: stackoverflow.com/questions/22483296/…
Frank TV
29

Terpecahkan!

Saya memutuskan untuk melihat apa yang mengubah atribut lebar dan tinggi yang saya atur javascriptuntuk melihat bagaimana hal itu memengaruhi ukuran kanvas - dan ternyata tidak. Ini mengubah resolusi.

Untuk mendapatkan hasil yang saya inginkan, saya juga harus mengatur canvas.style.widthatribut, yang mengubah ukuran fisik 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
Phil
sumber
1
Untuk hasil yang ideal, Anda tidak boleh menyentuh atribut gaya kanvas sama sekali dan hanya mengontrol ukuran dengan atribut lebar dan tinggi kanvas itu sendiri. Dengan cara itu Anda memastikan bahwa satu piksel di kanvas sama dengan satu piksel di layar.
Philipp
7
Saya tidak setuju. Mengubah atribut style.width / height persis seperti cara Anda membuat kanvas HiDPI.
MyNameIsKo
2
Dalam jawaban Anda, Anda menyetel canvas.width menjadi 1000 dan canvas.style.width menjadi setengah pada 500. Ini berfungsi tetapi hanya untuk perangkat dengan rasio piksel 2. Untuk apa pun di bawah itu, seperti monitor desktop Anda, kanvas sekarang menjadi menggambar ke piksel yang tidak perlu. Untuk rasio yang lebih tinggi, Anda sekarang kembali ke awal dengan aset / elemen resolusi rendah yang kabur. Masalah lain yang sepertinya disinggung oleh Philipp adalah bahwa semua yang Anda gambar ke konteks Anda sekarang harus ditarik ke lebar / tinggi dua kali lipat Anda meskipun itu ditampilkan pada setengah nilai itu. Perbaikan untuk ini adalah mengatur konteks kanvas Anda menjadi dua kali lipat.
MyNameIsKo
3
Ada window.devicePixelRatio, dan itu diterapkan dengan baik di sebagian besar browser modern.
Chen
6

Saya mengubah ukuran elemen kanvas melalui css untuk mengambil seluruh lebar elemen induk. Saya perhatikan bahwa lebar dan tinggi elemen saya tidak diskalakan. Saya mencari cara terbaik untuk mengatur ukuran yang seharusnya.

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

Dengan cara sederhana ini kanvas Anda akan diatur dengan sempurna, apa pun layar yang akan Anda gunakan.

Adam Mańkowski
sumber
5

Ini 100% menyelesaikannya untuk saya:

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

(ini dekat dengan solusi Adam Mańkowski).

Basj
sumber
4

Saya melihat detail yang tidak disebutkan dalam jawaban lain. Resolusi kanvas dipotong menjadi nilai integer.

Dimensi resolusi kanvas default adalah canvas.width: 300dan canvas.height: 150.

Di layar saya window.devicePixelRatio: 1.75,.

Jadi ketika saya mengatur canvas.height = 1.75 * 150nilainya terpotong dari yang diinginkan 262.5ke 262.

Solusinya adalah memilih dimensi tata letak CSS untuk diberikan window.devicePixelRatiosedemikian rupa sehingga pemotongan tidak akan terjadi pada penskalaan resolusi.

Misalnya, saya bisa menggunakan width: 300pxdan height: 152pxyang akan menghasilkan bilangan bulat jika dikalikan dengan 1.75.

Sunting : Solusi lain adalah memanfaatkan fakta bahwa piksel CSS dapat menjadi pecahan untuk melawan pemotongan piksel kanvas penskalaan.

Di bawah ini adalah demo menggunakan strategi ini.

Sunting : Berikut biola OP yang diperbarui untuk menggunakan strategi ini: 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>

spenceryue
sumber
3

Aku sedikit mengadaptasi MyNameIsKo kode di bawah canvg (SVG ke perpustakaan kanvas js). Saya sempat bingung dan meluangkan waktu untuk ini. Semoga ini bisa membantu seseorang.

HTML

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

Javascript

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);

}
Kurang Salah
sumber
2

Bagi Anda yang bekerja di reactjs, saya menyesuaikan jawaban MyNameIsKo dan itu berfungsi dengan baik. Ini kodenya.

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}/>
        )
    }
}

Dalam contoh ini, saya meneruskan lebar dan tinggi kanvas sebagai properti.

jrobins
sumber
2

Coba satu baris CSS ini di kanvas Anda: image-rendering: pixelated

Sesuai MDN :

Saat memperbesar gambar, algoritme tetangga terdekat harus digunakan, sehingga gambar tampak terdiri dari piksel besar.

Dengan demikian mencegah anti-aliasing sepenuhnya.

1valdis
sumber
2

Meskipun jawaban @ MyNameIsKo masih berfungsi, sekarang sedikit ketinggalan zaman di tahun 2020, dan dapat ditingkatkan:

function createHiPPICanvas(w, h) {
    let ratio = window.devicePixelRatio;
    let cv = document.createElement("canvas");
    cv.width = w * ratio;
    cv.height = h * ratio;
    cv.style.width = w + "px";
    cv.style.height = h + "px";
    cv.getContext("2d").scale(ratio, ratio);
    return cv;
}

Secara umum, kami melakukan peningkatan berikut:

  • Kami menghapus backingStorePixelRatioreferensi, karena ini tidak benar-benar diterapkan di browser apa pun dengan cara apa pun yang penting (pada kenyataannya, hanya Safari yang mengembalikan sesuatu selain undefined, dan versi ini masih berfungsi dengan sempurna di Safari);
  • Kami mengganti semua kode rasio itu dengan window.devicePixelRatio, yang memiliki dukungan fantastis
  • Ini juga berarti bahwa kita mendeklarasikan satu properti yang kurang global --- hore !!
  • Kami juga dapat menghapus || 1fallback window.devicePixelRatio, karena tidak ada gunanya: semua browser yang tidak mendukung properti ini tidak mendukung .setTransformatau .scalekeduanya, jadi fungsi ini tidak akan berfungsi, fallback atau tidak;
  • Kita dapat menggantinya .setTransformdengan .scale, karena mengirimkan lebar dan tinggi sedikit lebih intuitif daripada meneruskan matriks transformasi.
  • Fungsi telah diubah namanya dari createHiDPICanvasmenjadi createHiPPICanvas. Seperti @MyNameIsKo sendiri menyebutkan dalam komentar jawaban mereka, DPI (Dots per Inch) adalah terminologi pencetakan (karena printer membuat gambar dari titik-titik kecil tinta berwarna). Meskipun serupa, monitor menampilkan gambar menggunakan piksel, dan PPI (Pixels per Inch) adalah singkatan yang lebih baik untuk kasus penggunaan kami.

Satu keuntungan besar dari penyederhanaan ini adalah jawaban ini sekarang dapat digunakan di TypeScript tanpa // @ts-ignore(karena TS tidak memiliki tipe untuk backingStorePixelRatio).

Toastrackenigma
sumber
0

Bagi saya, hanya kombinasi dari berbagai teknik 'pixel perfect' yang membantu mengarsipkan hasil:

  1. Dapatkan dan skalakan dengan rasio piksel seperti yang disarankan @MyNameIsKo.

    pixelRatio = window.devicePixelRatio / ctx.backingStorePixelRatio

  2. Skala kanvas pada pengubahan ukuran (hindari penskalaan peregangan default kanvas).

  3. beberapa lineWidth dengan pixelRatio untuk menemukan ketebalan garis piksel 'nyata' yang tepat:

    context.lineWidth = ketebalan * rasio piksel;

  4. Periksa apakah ketebalan garisnya ganjil atau genap. tambahkan setengah dari rasio piksel ke posisi garis untuk nilai ketebalan ganjil.

    x = x + rasio piksel / 2;

Garis ganjil akan ditempatkan di tengah piksel. Garis di atas digunakan untuk menggerakkannya sedikit.

function getPixelRatio(context) {
  dpr = window.devicePixelRatio || 1,
    bsr = context.webkitBackingStorePixelRatio ||
    context.mozBackingStorePixelRatio ||
    context.msBackingStorePixelRatio ||
    context.oBackingStorePixelRatio ||
    context.backingStorePixelRatio || 1;

  return dpr / bsr;
}


var canvas = document.getElementById('canvas');
var context = canvas.getContext("2d");
var pixelRatio = getPixelRatio(context);
var initialWidth = canvas.clientWidth * pixelRatio;
var initialHeight = canvas.clientHeight * pixelRatio;


window.addEventListener('resize', function(args) {
  rescale();
  redraw();
}, false);

function rescale() {
  var width = initialWidth * pixelRatio;
  var height = initialHeight * pixelRatio;
  if (width != context.canvas.width)
    context.canvas.width = width;
  if (height != context.canvas.height)
    context.canvas.height = height;

  context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
}

function pixelPerfectLine(x) {

  context.save();
  context.beginPath();
  thickness = 1;
  // Multiple your stroke thickness  by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;

  context.strokeStyle = "Black";
  context.moveTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 0));
  context.lineTo(getSharpPixel(thickness, x), getSharpPixel(thickness, 200));
  context.stroke();
  context.restore();
}

function pixelPerfectRectangle(x, y, w, h, thickness, useDash) {
  context.save();
  // Pixel perfect rectange:
  context.beginPath();

  // Multiple your stroke thickness by a pixel ratio!
  context.lineWidth = thickness * pixelRatio;
  context.strokeStyle = "Red";
  if (useDash) {
    context.setLineDash([4]);
  }
  // use sharp x,y and integer w,h!
  context.strokeRect(
    getSharpPixel(thickness, x),
    getSharpPixel(thickness, y),
    Math.floor(w),
    Math.floor(h));
  context.restore();
}

function redraw() {
  context.clearRect(0, 0, canvas.width, canvas.height);
  pixelPerfectLine(50);
  pixelPerfectLine(120);
  pixelPerfectLine(122);
  pixelPerfectLine(130);
  pixelPerfectLine(132);
  pixelPerfectRectangle();
  pixelPerfectRectangle(10, 11, 200.3, 443.2, 1, false);
  pixelPerfectRectangle(41, 42, 150.3, 443.2, 1, true);
  pixelPerfectRectangle(102, 100, 150.3, 243.2, 2, true);
}

function getSharpPixel(thickness, pos) {

  if (thickness % 2 == 0) {
    return pos;
  }
  return pos + pixelRatio / 2;

}

rescale();
redraw();
canvas {
  image-rendering: -moz-crisp-edges;
  image-rendering: -webkit-crisp-edges;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  width: 100vh;
  height: 100vh;
}
<canvas id="canvas"></canvas>

Peristiwa pengubahan ukuran tidak diaktifkan di snipped sehingga Anda dapat mencoba file di github

Ievgen Naida
sumber
0

Bagi saya itu bukan hanya gambar tetapi teks memiliki kualitas yang buruk. Solusi kerja lintas browser paling sederhana untuk tampilan retina / non-retina adalah dengan membuat gambar dua kali lebih besar dari yang dimaksudkan dan menskalakan konteks kanvas seperti yang disarankan orang ini: https://stackoverflow.com/a/53921030/4837965

Polina Potrebko
sumber