Is there a 'fit to canvas' button in Photoshop Fw?

Canvas HTML: Stăpânind Redimensionarea și Adaptarea

16/05/2022

Rating: 4.25 (9069 votes)

Elementul HTML Canvas este o unealtă magică, un API de desenare puternic ce rulează peste tot și permite crearea de grafică dinamică și interactivă direct în paginile web. Cu toate acestea, în ciuda magiei sale, interacțiunea Canvasului cu CSS-ul poate fi uneori destul de complicată. Mulți dezvoltatori se întreabă cum să facă un canvas să umple întregul ecran, să se redimensioneze odată cu fereastra browserului sau să mențină un raport de aspect fix, scalându-se totodată pentru a se potrivi perfect într-o fereastră mai mică, fără suprapuneri. Toate aceste scenarii necesită o înțelegere profundă a modului în care funcționează Canvasul. Haideți să explorăm împreună aceste detalii esențiale.

How to make a canvas fit a small window?
We could use width:100% but the bottom might get cut off if the window is wide but short. Setting both width and height to 100% would make it fit but stretch and squish the pixels. Instead we want the canvas to be as big as possible while maintaining it’s aspect ratio, but not so big that any part of it is hidden.

Spre deosebire de alte elemente HTML, Canvasul este considerat un element înlocuit. Ce înseamnă asta? Ei bine, în esență, browserul tratează un element Canvas ca pe o imagine. Are o dimensiune internă fixă, definită prin atributele `width` și `height` direct pe elementul HTML, care reprezintă numărul de pixeli reali în care se desenează. Această analogie cu imaginea este crucială, deoarece multe stiluri CSS destinate imaginilor funcționează și pentru Canvas! Poți defini un canvas cu o dimensiune internă de, să zicem, 256x256 pixeli și apoi să-l scalezi vizual la 2560x2560 pixeli folosind CSS. Canvasul în sine nu 'știe' diferența; pur și simplu desenează în buffer-ul său intern, la fel cum o face o imagine.

Să luăm un exemplu simplu. Dacă ai un canvas de 256x256 pixeli și îi aplici stilul CSS `width: 8000px; height: auto;`, vei obține un canvas vizual enorm. În interior, desenul se realizează tot pe 256x256 pixeli, iar apoi browserul scalează acești pixeli la dimensiunea vizuală specificată de CSS. Acesta este un aspect fundamental al controlului dimensiunilor:

<canvas width="256" height="256"></canvas>
<style type='text/css'>
canvas {
width: 8000px;
height: auto;
}
</style>

Acest lucru creează o imagine foarte mare, dar cu o rezoluție internă scăzută, ceea ce poate duce la pixelare vizibilă dacă nu este gestionat corect.

Adaptarea pentru Ecrane cu DPI Ridicat (HiDPI)

Provocările apar atunci când dorim ca grafica noastră să arate impecabil pe ecranele moderne cu densitate mare de pixeli, cunoscute sub numele de ecrane HiDPI (High Dots Per Inch) sau Retina. Să presupunem că vrei un canvas care să ocupe vizual 500x500 pixeli, dar dacă utilizatorul are un ecran 2x HiDPI, vrei ca desenul să arate mult mai clar, nu pixelat. Soluția este să mărești numărul de pixeli interni ai canvasului și să folosești CSS pentru a-l constrânge înapoi la dimensiunea vizuală dorită.

În CSS, pixelii sunt 'pixeli virtuali' și sunt scalați automat de browser. Pe un ecran 2x HiDPI, un pixel CSS corespunde la 4 pixeli fizici (2x lățime, 2x înălțime). Deci, pentru un canvas vizual de 500x500 pixeli, ar trebui să setăm dimensiunea internă la 1000x1000 pixeli imagine, apoi să o scalăm înapoi la 500x500 pixeli virtuali prin CSS. Browserul va scala apoi acești 500x500 pixeli virtuali la 1000x1000 pixeli fizici, rezultând o imagine mult mai clară.

<canvas id="can" width="1000" height="1000"></canvas>
<style type='text/css'>
canvas {
width: 500px;
height: auto;
}
</style>
<script>
const ctx = document.getElementById('can').getContext('2d');
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(250, 250, 250, 0, Math.PI * 2);
ctx.fill();
</script>

Rețineți că raportul DPI nu este întotdeauna 2x; unele dispozitive mobile pot avea ecrane 3x sau chiar mai mult. De asemenea, dacă codul de desenare presupune 500px, dar canvasul are acum 1000px intern, desenul va ocupa doar colțul superior stânga. Soluția este să scalăm dimensiunea canvasului folosind `window.devicePixelRatio` și să inversăm scalarea pentru operațiile de desenare:

<canvas id="canDPI" width="500" height="500"></canvas>
<script>
const canvasDPI = document.getElementById('canDPI');
let dpi = window.devicePixelRatio || 1; // Fallback pentru browsere vechi
const WIDTH_VIRTUAL = 500;
const HEIGHT_VIRTUAL = 500;

// Setează dimensiunea internă a canvasului în funcție de DPI
canvasDPI.width = WIDTH_VIRTUAL * dpi;
canvasDPI.height = HEIGHT_VIRTUAL * dpi;

// Setează dimensiunea vizuală a canvasului prin CSS
canvasDPI.style.width = `${WIDTH_VIRTUAL}px`;
canvasDPI.style.height = `${HEIGHT_VIRTUAL}px`;

const ctxDPI = canvasDPI.getContext('2d');
ctxDPI.save(); // Salvează starea curentă a contextului
ctxDPI.scale(dpi, dpi); // Scalează contextul de desenare

// Desenează ca și cum dimensiunile ar fi virtuale (500x500)
ctxDPI.fillStyle = 'white';
ctxDPI.fillRect(0, 0, WIDTH_VIRTUAL, HEIGHT_VIRTUAL);
ctxDPI.beginPath();
ctxDPI.arc(WIDTH_VIRTUAL / 2, HEIGHT_VIRTUAL / 2, WIDTH_VIRTUAL / 2, 0, Math.PI * 2);
ctxDPI.fillStyle = 'magenta';
ctxDPI.fill();
ctxDPI.restore(); // Restaurează starea contextului
</script>

Această abordare asigură că desenul este realizat la rezoluția corectă pentru ecranul utilizatorului, menținând în același timp logica de desenare simplă, bazată pe dimensiunile virtuale.

Păstrarea Pixelilor "Grosolani" (Stil Retro)

Ce se întâmplă dacă dorim opusul clarității perfecte? Dacă dezvoltăm un joc cu artă pixelată în stil retro și vrem să vedem pixelii "chonky" (grosolani), fără netezire? Dacă scalăm pur și simplu un canvas de rezoluție mică la o dimensiune mare, pixelii vor deveni neclari. Din fericire, CSS-ul ne vine în ajutor cu proprietatea `image-rendering: pixelated;` care funcționează și pentru elementele Canvas.

<style type='text/css'>
canvas {
width: 500px;
height: 500px;
image-rendering: pixelated; /* Magia pixelilor clari */
background-color: lightgray;
}
</style>
<canvas id="retroCan" width="16" height="16"></canvas>
<script>
const retroCan = document.getElementById('retroCan');
const ctxRetro = retroCan.getContext('2d');
// Desenează un pătrat 2x2 pixeli pe un canvas 16x16
ctxRetro.fillStyle = 'blue';
ctxRetro.fillRect(7, 7, 2, 2);
</script>

Cu această singură proprietate CSS, pixelii vor rămâne clari și definiți, oferind aspectul retro dorit. Este important de menționat că, dacă desenezi imagini scalate în interiorul canvasului, acestea ar putea fi totuși netezite. Poți remedia acest lucru setând `ctx.imageSmoothingEnabled = false;` pe contextul de desenare pentru un aspect cu adevărat pixelat.

Menținerea Raportului de Aspect cu `object-fit`

Acum, să zicem că avem un canvas pentru un joc retro de 160x120 pixeli și vrem să-l facem cât mai mare posibil, pentru a umple spațiul disponibil în pagină, dar fără a distorsiona imaginea. Dacă am folosi `width: 100%;` și `height: 100%;`, canvasul s-ar întinde și s-ar deforma, stricând raportul de aspect. Soluția este să folosim proprietatea CSS `object-fit`.

Why does my image not fit in Photoshop?
When you import an image into Photoshop or transport a new image to your canvas, many times that image doesn’t fit it. This is because Photoshop doesn’t automatically fit your image to the canvas, so you have to rescale the image to fit your canvas.

Proprietatea `object-fit` este incredibil de utilă pentru a controla modul în care un element înlocuit (cum ar fi o imagine sau un canvas) trebuie să se potrivească în caseta sa de conținut. Există mai multe valori, dar cele mai relevante aici sunt `contain` și `cover`.

Să explorăm aceste valori într-un tabel comparativ:

Valoare `object-fit`DescriereEfect asupra Canvasului
`contain`Elementul este scalat pentru a se potrivi complet în caseta sa de conținut, menținând raportul de aspect. Poate lăsa spațiu gol (bare negre) pe una dintre axe.Canvasul se va micșora pentru a fi complet conținut în dimensiunea stilizată, fără a fi decupat. Ideal pentru jocuri sau grafice unde tot conținutul trebuie să fie vizibil.
`cover`Elementul este scalat pentru a umple complet caseta sa de conținut, menținând raportul de aspect. Părți din element pot fi decupate.Canvasul se va extinde pentru a umple tot spațiul, dar marginile pot fi decupate. Utile pentru fundaluri sau elemente decorative unde nu contează dacă o parte din conținut este ascunsă.
`fill`Elementul este scalat pentru a umple complet caseta de conținut, ignorând raportul de aspect.Canvasul se va întinde sau se va comprima pentru a umple spațiul, deformând imaginea. De evitat dacă raportul de aspect este crucial.
`none`Elementul nu este scalat.Canvasul își păstrează dimensiunea internă, indiferent de dimensiunea casetei de conținut.
`scale-down`Elementul este scalat ca și `none` sau `contain`, oricare rezultă într-o dimensiune de obiect mai mică.Utile pentru a asigura că elementul nu depășește niciodată dimensiunea sa intrinsecă.

Pentru exemplul nostru de joc retro (160x120px) într-o zonă de 500x500px:

<style type='text/css'>
canvas.fit-example {
width: 500px;
height: 500px;
image-rendering: pixelated;
background-color: black; /* Se va vedea în spațiul gol */
}
canvas.fit-contain {
object-fit: contain;
}
canvas.fit-cover {
object-fit: cover;
}
</style>

<canvas class="fit-example fit-contain" width="160" height="120"></canvas>
<p>Cu <code>object-fit: contain;</code> - canvasul se potrivește, lăsând bare negre.</p>

<canvas class="fit-example fit-cover" width="160" height="120"></canvas>
<p>Cu <code>object-fit: cover;</code> - canvasul umple, dar decupează marginile.</p>

Cu `object-fit: contain`, canvasul se va micșora pentru a fi complet conținut în dimensiunea stilizată (500x500px), lăsând bare negre (sau culoarea de fundal a elementului canvas) pentru a umple spațiul suplimentar. Aceasta este adesea soluția ideală pentru a asigura că întregul conținut al canvasului este vizibil, menținând în același timp raportul de aspect. Această proprietate CSS este o adevărată salvare pentru un design object-fit.

Canvas care Umple Întregul Ecran/Fereastră

Până acum, am folosit canvasuri cu o dimensiune fixă, pe care le-am scalat vizual. Dar ce facem dacă vrem ca un canvas să se adapteze complet la dimensiunea ferestrei browserului, de exemplu, pentru o aplicație de desenare sau un joc fullscreen? Aici vom avea nevoie de puțin cod JavaScript.

Dacă am da canvasului `width: 100%;` și `height: 100%;`, acesta s-ar întinde să umple spațiul disponibil, dar ar deforma imaginea. Trebuie să schimbăm atât dimensiunea vizuală (pixeli CSS), cât și suprafața de desenare (pixeli interni ai imaginii) pentru a gestiona condițiile curente.

Primul pas este să ne asigurăm că un element (de exemplu, un `div` care înconjoară canvasul) umple pagina. Setarea `width: 100%;` și `height: 100%;` pentru `div` nu este suficientă, deoarece elementul `body` are o înălțime care depinde de conținut. Ceea ce vrem este ca înălțimea să fie exact 100% din înălțimea viewport-ului. Aici intervin unitățile CSS `vh` (viewport height) și `vw` (viewport width).

<style>
html, body {
margin: 0;
padding: 0;
overflow: hidden; /* Previne barele de scroll */
}
#wrapper {
width: 100vw; /* Ocupă 100% din lățimea viewport-ului */
height: 100vh; /* Ocupă 100% din înălțimea viewport-ului */
border: 5px solid gray;
box-sizing: border-box; /* Include padding și border în lățime/înălțime */
}
#canFull {
display: block;
}
</style>

<div id="wrapper">
<canvas id="canFull"></canvas>
</div>

Acum, că `div`-ul nostru de învelire se redimensionează corect, trebuie să actualizăm dimensiunile canvasului însuși ori de câte ori fereastra este redimensionată. Vom folosi JavaScript pentru a face acest lucru. La fiecare redimensionare, vom seta dimensiunea internă a canvasului la dimensiunea `div`-ului, minus orice margini sau borduri (în cazul nostru, bordura de 5px).

<script>
function calcCanvasSize() {
// Obține dimensiunile elementului wrapper
const wrapper = document.getElementById('wrapper');
let rect = wrapper.getBoundingClientRect();
const can = document.getElementById('canFull');

// Setează dimensiunea internă a canvasului (pixeli de desenare) la dimensiunea wrapper-ului,
// scăzând bordurile pentru a evita barele de scroll
can.width = rect.width - 5 * 2; // Scade 5px pe stânga și 5px pe dreapta
can.height = rect.height - 5 * 2; // Scade 5px pe sus și 5px pe jos

const WIDTH_CAN = can.width;
const HEIGHT_CAN = can.height;

// Redesenează conținutul
const ctx = can.getContext('2d');
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, WIDTH_CAN, HEIGHT_CAN);
ctx.beginPath();
ctx.arc(WIDTH_CAN / 2, HEIGHT_CAN / 2, Math.min(WIDTH_CAN, HEIGHT_CAN) / 2 - 10, 0, Math.PI * 2); // Adaptăm raza
ctx.fillStyle = 'magenta';
ctx.fill();
}

// Apelez funcția o dată la încărcarea paginii
calcCanvasSize();
// Adaugă un eveniment listener pentru redimensionarea ferestrei
window.addEventListener('resize', calcCanvasSize);
</script>

Acest cod asigură un canvas care se redimensionează frumos, adaptându-se la dimensiunea ferestrei și la ecranele HiDPI, menținând în același timp raportul de aspect și claritatea. Este o soluție robustă pentru aplicații web fullscreen.

Exemple Practice și Cod Suplimentar

Pentru a consolida înțelegerea, iată un exemplu complet care demonstrează un canvas responsive ce se adaptează la dimensiunea ferestrei, similar cu cel discutat anterior, dar într-un format mai concis:

<!DOCTYPE html>
<html lang="ro">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas Responsiv</title>
<style>
body {
margin: 0;
overflow: hidden; /* Important pentru a evita barele de scroll */
background-color: #333;
}
canvas.responsive-example {
display: block; /* Elimină spațiul suplimentar de sub canvas */
}
</style>
</head>
<body>
<canvas id="myResponsiveCanvas" class="responsive-example"></canvas>

<script>
const canvasResponsive = document.getElementById('myResponsiveCanvas');
const ctxResponsive = canvasResponsive.getContext('2d');

// Funcția pentru a redimensiona canvasul la dimensiunea ferestrei
function resizeCanvasResponsive() {
// Ajustează dimensiunile interne ale canvasului
// pentru a ține cont de DPI, dacă este necesar
const dpr = window.devicePixelRatio || 1;
canvasResponsive.width = window.innerWidth * dpr;
canvasResponsive.height = window.innerHeight * dpr;

// Ajustează dimensiunea vizuală a canvasului prin CSS
canvasResponsive.style.width = `${window.innerWidth}px`;
canvasResponsive.style.height = `${window.innerHeight}px`;

// Scalează contextul de desenare pentru a compensa DPI
ctxResponsive.scale(dpr, dpr);

drawResponsive(); // Redesenează conținutul după redimensionare
}

// Funcția pentru a desena pe canvas
function drawResponsive() {
// Curăță canvasul
ctxResponsive.clearRect(0, 0, canvasResponsive.width, canvasResponsive.height);
// Ex: Desenează un cerc care umple spațiul disponibil
ctxResponsive.fillStyle = 'blue';
const centerX = canvasResponsive.width / (2 * (window.devicePixelRatio || 1));
const centerY = canvasResponsive.height / (2 * (window.devicePixelRatio || 1));
const radius = Math.min(centerX, centerY) * 0.8;
ctxResponsive.beginPath();
ctxResponsive.arc(centerX, centerY, radius, 0, Math.PI * 2);
ctxResponsive.fill();
}

// Setează dimensiunea inițială a canvasului
resizeCanvasResponsive();

// Apelez resizeCanvasResponsive() când fereastra este redimensionată
window.addEventListener('resize', resizeCanvasResponsive);
</script>
</body>
</html>

Întrebări Frecvente (FAQ)

De ce se întinde canvasul meu când redimensionez fereastra?
Acest lucru se întâmplă de obicei când setați `width: 100%;` și `height: 100%;` prin CSS fără a ajusta dimensiunile interne ale canvasului sau fără a menține raportul de aspect. Canvasul este tratat ca o imagine și se va deforma dacă raportul de aspect vizual nu corespunde cu cel intern. Soluția este să folosiți `object-fit: contain` sau să ajustați dimensiunile interne (`canvas.width`, `canvas.height`) și să redesenați conținutul la fiecare redimensionare.
Cum fac canvasul să arate clar pe ecranele Retina/HiDPI?
Pentru a asigura claritate pe ecranele HiDPI, trebuie să setați dimensiunile interne ale canvasului (atributele `width` și `height` ale elementului) la un multiplu al dimensiunii vizuale, folosind `window.devicePixelRatio`. Apoi, scalați contextul de desenare (`ctx.scale(dpi, dpi)`) pentru a compensa această mărire internă, astfel încât logica de desenare să rămână simplă, bazată pe dimensiunile virtuale.
Pot face un canvas să arate ca un joc retro, cu pixeli mari și clari?
Da, absolut! Pentru a obține un aspect pixelat, fără netezire, folosiți proprietatea CSS `image-rendering: pixelated;` pe elementul canvas. De asemenea, puteți seta `ctx.imageSmoothingEnabled = false;` pe contextul de desenare pentru a preveni netezirea imaginilor desenate în interiorul canvasului.
Afectează performanța redimensionarea constantă a canvasului?
Da, redimensionarea și mai ales redesenarea constantă a întregului conținut al canvasului la fiecare eveniment `resize` poate afecta performanța, în special pentru grafice complexe. Pentru optimizare, puteți folosi o tehnică de 'debounce' sau 'throttle' pentru evenimentul `resize`, astfel încât funcția de redesenare să fie apelată doar o dată după ce utilizatorul a terminat de redimensionat fereastra, sau la un interval fix de timp.
Ce este diferența dintre `canvas.width` și `canvas.style.width`?
`canvas.width` și `canvas.height` (atributele HTML) definesc numărul de pixeli intrinseci ai suprafeței de desenare a canvasului, adică rezoluția sa internă. `canvas.style.width` și `canvas.style.height` (proprietățile CSS) definesc dimensiunea vizuală a elementului canvas în document, adică cât de mare apare pe ecran. Este crucial să le înțelegeți diferența pentru a gestiona corect aspectul și claritatea Canvasului.

Concluzie

Sperăm că prin acest articol ați înțeles cât de ușor este să stilizați și să adaptați elementul Canvas pentru diverse utilizări. Pe măsură ce browserele introduc mai multe proprietăți CSS pentru imagini, aproape toate vor funcționa și pentru elementele Canvas, oferind o flexibilitate extraordinară. De la gestionarea ecranelor HiDPI la crearea de jocuri retro cu pixeli clari și până la construirea de aplicații fullscreen responsive, Canvasul este o unealtă incredibil de puternică. Mergeți și creați minuni cu Canvas!

Dacă vrei să descoperi și alte articole similare cu Canvas HTML: Stăpânind Redimensionarea și Adaptarea, poți vizita categoria Fitness.

Go up