Ricerca…
Creazione di una tela a pagina intera reattiva
Codice di avviamento per creare e rimuovere un'area di disegno pagina completa che risponde a ridimensionare gli eventi tramite javascript.
var canvas; // Global canvas reference
var ctx; // Global 2D context reference
// Creates a canvas
function createCanvas () {
const canvas = document.createElement("canvas");
canvas.style.position = "absolute"; // Set the style
canvas.style.left = "0px"; // Position in top left
canvas.style.top = "0px";
canvas.style.zIndex = 1;
document.body.appendChild(canvas); // Add to document
return canvas;
}
// Resizes canvas. Will create a canvas if it does not exist
function sizeCanvas () {
if (canvas === undefined) { // Check for global canvas reference
canvas = createCanvas(); // Create a new canvas element
ctx = canvas.getContext("2d"); // Get the 2D context
}
canvas.width = innerWidth; // Set the canvas resolution to fill the page
canvas.height = innerHeight;
}
// Removes the canvas
function removeCanvas () {
if (canvas !== undefined) { // Make sure there is something to remove
removeEventListener("resize", sizeCanvas); // Remove resize event
document.body.removeChild(canvas); // Remove the canvas from the DOM
ctx = undefined; // Dereference the context
canvas = undefined; // Dereference the canvas
}
}
// Add the resize listener
addEventListener("resize", sizeCanvas);
// Call sizeCanvas to create and set the canvas resolution
sizeCanvas();
// ctx and canvas are now available for use.
Se non hai più bisogno della tela, puoi rimuoverla chiamando removeCanvas()
Una demo di questo esempio su jsfiddle
Coordinate del mouse dopo il ridimensionamento (o lo scorrimento)
Le app su tela spesso si basano molto sull'interazione dell'utente con il mouse, ma quando la finestra viene ridimensionata, le coordinate del mouse su cui si basa il canvas sono probabilmente modificate perché il ridimensionamento fa sì che l'area di disegno venga sfalsata in una posizione diversa rispetto alla finestra. Pertanto, la progettazione reattiva richiede che la posizione di offset della tela venga ricalcolata quando la finestra viene ridimensionata e anche ricalcolata quando si scorre la finestra.
Questo codice è in ascolto degli eventi di ridimensionamento delle finestre e ricalcola gli offset utilizzati nei gestori di eventi del mouse:
// variables holding the current canvas offset position
// relative to the window
var offsetX,offsetY;
// a function to recalculate the canvas offsets
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
// listen for window resizing (and scrolling) events
// and then recalculate the canvas offsets
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
// example usage of the offsets in a mouse handler
function handleMouseUp(e){
// use offsetX & offsetY to get the correct mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// ...
}
Animazioni su tela reattive senza ridimensionare gli eventi.
Gli eventi di ridimensionamento della finestra possono essere attivati in risposta al movimento del dispositivo di input dell'utente. Quando ridimensioni un quadro, questo viene automaticamente cancellato e sei costretto a ri-renderizzare il contenuto. Per le animazioni fai questo ogni fotogramma tramite la funzione del ciclo principale chiamata da requestAnimationFrame
che fa del suo meglio per mantenere il rendering sincronizzato con l'hardware del display.
Il problema con l'evento di ridimensionamento è che quando il mouse viene utilizzato per ridimensionare la finestra, gli eventi possono essere attivati molte volte più velocemente rispetto alla velocità standard di 60 fps del browser. Quando l'evento di ridimensionamento termina, il buffer di back nell'area di disegno viene presentato al DOM non sincronizzato con il dispositivo di visualizzazione, che può causare cesoie e altri effetti negativi. C'è anche un sacco di allocazione e rilascio della memoria inutili che possono ulteriormente incidere sull'animazione quando GC si ripulisce qualche tempo dopo.
Evento di ridimensionamento rimbalzato
Un modo comune per gestire le alte velocità di attivazione dell'evento di ridimensionamento è quello di eliminare l'evento di ridimensionamento.
// Assume canvas is in scope
addEventListener.("resize", debouncedResize );
// debounce timeout handle
var debounceTimeoutHandle;
// The debounce time in ms (1/1000th second)
const DEBOUNCE_TIME = 100;
// Resize function
function debouncedResize () {
clearTimeout(debounceTimeoutHandle); // Clears any pending debounce events
// Schedule a canvas resize
debounceTimeoutHandle = setTimeout(resizeCanvas, DEBOUNCE_TIME);
}
// canvas resize function
function resizeCanvas () { ... resize and redraw ... }
L'esempio precedente ritarda il ridimensionamento della tela fino a 100ms dopo l'evento di ridimensionamento. Se in quel momento vengono attivati ulteriori eventi di ridimensionamento, il timeout di ridimensionamento esistente viene annullato e ne viene pianificato uno nuovo. Questo effettivamente consuma la maggior parte degli eventi di ridimensionamento.
Ha ancora alcuni problemi, il più notevole è il ritardo tra il ridimensionamento e la visualizzazione della tela ridimensionata. La riduzione del tempo di rimbalzo migliora questo ma il ridimensionamento non è ancora sincronizzato con il dispositivo di visualizzazione. Hai anche il rendering dell'anello principale dell'animazione su una tela inadeguata.
Più codice può ridurre i problemi! Più codice crea anche i suoi nuovi problemi.
Semplice e il migliore ridimensionamento
Avendo provato molti modi diversi per appianare il ridimensionamento della tela, dall'assurdamente complesso, per ignorare il problema (chi se ne importa comunque?) Mi sono ricollegato ad un fidato amico.
KISS è qualcosa di cui la maggior parte dei programmatori dovrebbe essere a conoscenza (( K eep I t S imple S tupid) Lo stupido si riferisce a me per non averci pensato anni fa. ) E si scopre che la soluzione migliore è la più semplice di tutte.
Ridimensiona la tela dall'interno del ciclo dell'animazione principale. Rimane sincronizzato con il dispositivo di visualizzazione, non c'è rendering inutile e la gestione delle risorse è al minimo possibile mantenendo la frequenza fotogrammi completa. Né è necessario aggiungere un evento di ridimensionamento alla finestra o altre funzioni di ridimensionamento aggiuntive.
Si aggiunge il ridimensionamento in cui normalmente si cancella la tela controllando se le dimensioni della tela corrispondono alle dimensioni della finestra. Se non lo ridimensiona.
// Assumes canvas element is in scope as canvas
// Standard main loop function callback from requestAnimationFrame
function mainLoop(time) {
// Check if the canvas size matches the window size
if (canvas.width !== innerWidth || canvas.height !== innerHeight) {
canvas.width = innerWidth; // resize canvas
canvas.height = innerHeight; // also clears the canvas
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height); // clear if not resized
}
// Animation code as normal.
requestAnimationFrame(mainLoop);
}