Saya total n00b dengan HTML5
dan saya bekerja dengan canvas
untuk 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 CSS
akan 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
Biola
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.
sumber
clearRect
.Jawaban:
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!
sumber
Terpecahkan!
Saya memutuskan untuk melihat apa yang mengubah atribut lebar dan tinggi yang saya atur
javascript
untuk 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.width
atribut, yang mengubah ukuran fisikcanvas
: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
sumber
window.devicePixelRatio
, dan itu diterapkan dengan baik di sebagian besar browser modern.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.
Dengan cara sederhana ini kanvas Anda akan diatur dengan sempurna, apa pun layar yang akan Anda gunakan.
sumber
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).
sumber
Saya melihat detail yang tidak disebutkan dalam jawaban lain. Resolusi kanvas dipotong menjadi nilai integer.
Dimensi resolusi kanvas default adalah
canvas.width: 300
dancanvas.height: 150
.Di layar saya
window.devicePixelRatio: 1.75
,.Jadi ketika saya mengatur
canvas.height = 1.75 * 150
nilainya terpotong dari yang diinginkan262.5
ke262
.Solusinya adalah memilih dimensi tata letak CSS untuk diberikan
window.devicePixelRatio
sedemikian rupa sehingga pemotongan tidak akan terjadi pada penskalaan resolusi.Misalnya, saya bisa menggunakan
width: 300px
danheight: 152px
yang akan menghasilkan bilangan bulat jika dikalikan dengan1.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>
sumber
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); }
sumber
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.
sumber
Coba satu baris CSS ini di kanvas Anda:
image-rendering: pixelated
Sesuai MDN :
Dengan demikian mencegah anti-aliasing sepenuhnya.
sumber
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:
backingStorePixelRatio
referensi, karena ini tidak benar-benar diterapkan di browser apa pun dengan cara apa pun yang penting (pada kenyataannya, hanya Safari yang mengembalikan sesuatu selainundefined
, dan versi ini masih berfungsi dengan sempurna di Safari);window.devicePixelRatio
, yang memiliki dukungan fantastis|| 1
fallbackwindow.devicePixelRatio
, karena tidak ada gunanya: semua browser yang tidak mendukung properti ini tidak mendukung.setTransform
atau.scale
keduanya, jadi fungsi ini tidak akan berfungsi, fallback atau tidak;.setTransform
dengan.scale
, karena mengirimkan lebar dan tinggi sedikit lebih intuitif daripada meneruskan matriks transformasi.createHiDPICanvas
menjadicreateHiPPICanvas
. 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 untukbackingStorePixelRatio
).sumber
Bagi saya, hanya kombinasi dari berbagai teknik 'pixel perfect' yang membantu mengarsipkan hasil:
Dapatkan dan skalakan dengan rasio piksel seperti yang disarankan @MyNameIsKo.
pixelRatio = window.devicePixelRatio / ctx.backingStorePixelRatio
Skala kanvas pada pengubahan ukuran (hindari penskalaan peregangan default kanvas).
beberapa lineWidth dengan pixelRatio untuk menemukan ketebalan garis piksel 'nyata' yang tepat:
context.lineWidth = ketebalan * rasio piksel;
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
sumber
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
sumber