Responsive full width canvas in sveltejs - javascript

I'm quite new to svelte and I'm trying to get a canvas to render on the full screen using svelte. Sounds quite easy to do, but I can't get it to work properly. I'm binding a width and height variable to the clientWidth/clientHeight of the parent and using these variables to set the dimensions of the canvas. The issue now is that when onMount is called, the width and height variables are set but they are not applied to the canvas element yet. This means when the canvas renders for the first time, it still has the initial dimensions and not the ones of the parent. Only when I render it a second time it has the proper dimensions. How can get the canvas to have the right dimensions on the first render or render the canvas again when it has the proper dimensions?
Here you can find a "working" version.
<script>
import { onMount } from "svelte";
let canvas;
let ctx;
let width = 1007;
let height = 1140;
const draw = () => {
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.moveTo(width/2 - 50, height/2);
ctx.arc(width/2, height/2, 50, 0, 2 * Math.PI);
ctx.fill();
}
onMount(() => {
ctx = canvas.getContext("2d");
draw();
setTimeout(draw, 5000);
});
</script>
<style>
.container {
width: 100%;
height: 100%;
}
</style>
<div
class="container"
bind:clientWidth={width}
bind:clientHeight={height}>
<canvas bind:this={canvas} {width} {height} />
</div>

You can solve this by waiting for the next 'tick'
import { onMount, tick } from 'svelte';
onMount(async () => {
ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;
await tick()
draw();
});
What await tick() does is to effectively pause execution until all current changes have been applied to the dom
tick in the docs

Related

Canvas rendering from component is not working on Svelte

I'm trying to render canvas and draw rectangle on it using svelte.js, but it does not work if I write rendering code on the component side.
Here is the reproduction REPL.
It works if I move entire code in Canvas.svelte to app.svelte.
App.svelte:
<script>
import Canvas from './Canvas.svelte';
</script>
<Canvas/>
Canvas.svelte:
<script>
import { onMount } from 'svelte';
let canvasEl;
const canvas = {
width: 1000,
height: 1000
};
const drawRect = (context) => {
context.beginPath();
context.fillStyle = '#000000';
context.rect(0, 0, 40, 10);
context.fill();
};
onMount(() => {
console.log('Onmount');
const context = canvasEl.getContext('2d');
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
drawRect(context);
});
</script>
<canvas bind:this={canvasEl} width={canvas.width} height={canvas.height} />
Does anyone know the solution?
Thank you,
Didn't find a reason of error, but found the source:
onMount(() => {
//...
// this 2 lines cause silent error
canvas.width = document.body.clientWidth;
canvas.height = document.body.clientHeight;
//...
});
Remove them and you will see the canvas and rect

html5 canvas resize with javascript (not achieving desired result)

i was testing some code with javascript, about resizing the html5 canvas. i've asked similar question before, when i was testing the same thing with jquery. and i've got the desired solution that time, reference (eg_1). in this (eg_1), the result was, canvas element was resizing in every frame because of requestAnimationFrame($.fn.animate); is taking place in a recursive form.the visual result for that is here the width attribute updates when i resize the console window. and if i close the console the canvas resizes automatically, i don't need to reload the window, to update the attribute values. means canvas is full screen, all time. but i am having problem with the same thing, in javascript. if i close the console, the width of the console cuts (or resizes) the canvas with a white space. here is the visual result the white space is my problem. i've to reload the window, to make the canvas full screen again. in short i'm not having the same effect as (eg_1). here i tried so far, reference (eg_2).
(eg_1)
html : <canvas></canvas>
css :
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
jquery :
$(document).ready(function() {
var view = $(window),
canvas = $("canvas"),
ctx = canvas[0].getContext("2d"),
width = view.width(),
height = view.height();
$.fn.windowResize = function() {
var width = view.width();
height = view.height();
canvas.attr("width", width);
canvas.attr("height", height);
};
view.on("resize", $.fn.windowResize);
canvas.attr("width", width);
canvas.attr("height", height);
$.fn.animate = function() {
requestAnimationFrame($.fn.animate);
ctx.clearRect(0, 0, width, height);
// start here
};
$.fn.animate();
});
(eg_2)
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;
function resize() {
setInterval(resize, 5);
clearInterval(resize);
canvas.width = windowWidth;
canvas.height = windowHeight;
}
resize();
Answer:
Your initial issue:
You need to get the updated values of window.innerWidth and window.innerHeight.
To do this you can turn the variables into functions that request the current values.
Example:
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = () => window.innerWidth;
var windowHeight = () => window.innerHeight;
function resize() {
setInterval(resize, 5);
clearInterval(resize);
canvas.width = windowWidth();
canvas.height = windowHeight();
}
resize();
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
<canvas></canvas>
Other issues:
Your use of Timers is incorrect. clearInterval(resize) isn't going to clear anything, because resize is not a timer itself.
Secondly you shouldn't be using any timers when rendering to Canvas. It can often lead to things like the above( multiple timers ) or issues with constant, unnecessary, re-processing.
Instead of resizing perpetually you may want to just do so when the resize event is dispatched. Even if you were using requestAnimationFrame - which would effectively stop the render if anything else takes precedence or if you switch tabs, it's an unnecessary process in that you're constantly re-rendering the same thing without cause. Canvas Apps tend to get big real quick when you add in multiple rendering functions, so you'll want to mitigate unnecessary steps.
You can do this by attaching to appropriate events and creating requestAnimationFrame helpers like in the following example:
var canvas = document.querySelectorAll("canvas")[0];
var context = canvas.getContext("2d");
var windowWidth = () => window.innerWidth;
var windowHeight = () => window.innerHeight;
var request = fn => requestAnimationFrame(fn),
requestHandler = fn => () => request(fn);
function resize() {
canvas.width = windowWidth();
canvas.height = windowHeight();
}
request(resize);
window.addEventListener("resize", requestHandler(resize) );
* {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
background-color: turquoise;
}
<canvas></canvas>

Using window.scrollY for canvas calculations causes issues

I've been trying to make a parallax-esque effect using HTML5 canvas, for various reasons. I've been using the window.scrollY property to determine how far down the user has scrolled and therefore can calculate the transformations using this value.
I suppose this is best explained through an example:
function draw() {
scrollOffset = window.scrollY;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#000";
ctx.fillRect((width/2) - (size/2), ((height/2) - (size/2)) + scrollOffset, size, size);
requestAnimationFrame(draw);
}
As you can see, depending on what browser you use, there will be a varying level of "glitchiness" caused by you scrolling. The square in the center should remain in the same place the entire time, however there are issues with keeping up with how far the user has scrolled.
How noticeable this problem is depends on what browser you are using; Chrome is only noticeable if you change your scrolling direction in rapid succession whereas Firefox and Edge are noticeable regardless of how slowly you scroll.
Would there be a better way to go about this?
Note: Not a solution but a suggestion
The parallax should happen only when one scrolls on the page. Should be something like the snippet below:
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let width;
let height;
window.addEventListener("resize", resizeCanvas);
function resizeCanvas() {
width = canvas.width = document.documentElement.clientWidth;
height = canvas.height = document.documentElement.clientHeight;
}
resizeCanvas();
let scrollOffset;
let size = 50;
function draw() {
scrollOffset = window.scrollY;
ctx.clearRect(0, 0, width, height);
ctx.fillStyle = "#000";
ctx.fillRect((width/2) - (size/2), ((height/2) - (size/2)) + scrollOffset, size, size);
}
draw();
window.onscroll = function (e)
{
draw();
}
html, body {
margin: 0;
}
canvas {
display: block;
}
#padder {
height: 100vh;
background-color: #F00;
}
<canvas id="canvas"></canvas>
<section id="padder"></section>
Also, since it's browser specific, for Firefox, check scrolling options in the advanced settings or you shall try changing the mouseWheel scrolling behavior in about:config

Getting canvas width trouble

Okay. So I have a canvas with an id of mainCanvas, with javascript and css on the same html page. I want to have it so that I can make things a certain width of the canvas, in javascript (Example, a red rectangle be the width and height of the canvas). So I have the canvas width set to 60% and the height set to 500px (the height isn't the problem) in css, and I have the code,
var canvas = document.getElementById("mainCanvas");
var context = document.getContext("2d");
var width = canvas.width;
var height = canvas.height;
which gets the canvas width and height. Then I have a setInterval() function with a game function that calls and update function, and a render function.
function game() {
update();
render();
}
function update() {
}
function render() {
context.fillStyle = "red";
context.fillRect(0, 0, width, height);
}
setInterval(function() {
game();
}, 1000/30);
So I have a rectangle that has the width variable as the width parameter, and the height variable as the height parameter.
Just so it is easier to see, here is a snippet.
<html>
<head>
<title></title>
<style type="text/css">
body {
background-color: #222222;
}
canvas {
background-color: #1f1f1f;
width: 60%;
height: 500px;
}
</style>
</head>
<body>
<canvas id="mainCanvas"></canvas>
<script type="text/javascript">
var canvas = document.getElementById("mainCanvas");
var context = document.getContext("2d");
var width = canvas.width;
var height = canvas.height;
function game() {
update();
render();
}
function update() {
}
function render() {
context.fillStyle = "red";
context.fillRect(0, 0, width, height);
}
setInterval(function() {
game();
}, 1000/30);
</script>
</body>
</html>
Why is the width and height not working? I have seen many questions on stackoverflow but those don't fix my problem.
Change the line:
var context = document.getContext("2d");
To:
var context = canvas.getContext("2d");
Change it to:
var canvas = document.getElementById("mainCanvas");
var context = canvas.getContext("2d"); // `canvas` instead of `document`
var width = canvas.width;
var height = canvas.height;

HTML canvas - drawing disappear on resizing

I have created a basic shape in HTML canvas element which works fine.
The problem occurs when I resize the canvas, all the drawing in the canvas disappears. Is this the normal behavior? or is there a function that can be used to stop this?
One way to fix this could be to call drawing function again on canvas resize however this may not be very efficient if there is huge content to be drawn.
What's the best way?
Here is the link to sample code https://gist.github.com/2983915
You need to redraw the scene when you resize.
setting the width or height of a canvas, even if you are setting it to the same value as before, not only clears the canvas but resets the entire canvas context. Any set properties (fillStyle, lineWidth, the clipping region, etc) will also be reset.
If you do not have the ability to redraw the scene from whatever data structures you might have representing the canvas, you can always save the entire canvas itself by drawing it to an in-memory canvas, setting the original width, and drawing the in-memory canvas back to the original canvas.
Here's a really quick example of saving the canvas bitmap and putting it back after a resize:
http://jsfiddle.net/simonsarris/weMbr/
Everytime you resize the canvas it will reset itself to transparant black, as defined in the spec.
You will either have to:
redraw when you resize the canvas, or,
don't resize the canvas
One another way is to use the debounce if you are concerned with the performance.
It doesnt resize or redraw every position you are dragging. But it will resize only when the it is resized.
// 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 ... }
I had the same problem. Try following code
var wrapper = document.getElementById("signature-pad");
var canvas = wrapper.querySelector("canvas");
var ratio = Math.max(window.devicePixelRatio || 1, 1);
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
It keeps the drawing as it is
One thing that worked for me was to use requestAnimationFrame().
let height = window.innerHeight;
let width = window.innerWidth;
function handleWindowResize() {
height = window.innerHeight;
width = window.innerWidth;
}
function render() {
// Draw your fun shapes here
// ...
// Keep this on the bottom
requestAnimationFrame(render);
}
// Canvas being defined at the top of the file.
function init() {
ctx = canvas.getContext("2d");
render();
}
I had the same problem when I had to resize the canvas to adjust it to the screen.
But I solved it with this code:
var c = document.getElementById('canvas');
ctx = c.getContext('2d');
ctx.fillRect(0,0,20,20);
// Save canvas settings
ctx.save();
// Save canvas context
var dataURL = c.toDataURL('image/jpeg');
// Resize canvas
c.width = 50;
c.height = 50;
// Restore canvas context
var img = document.createElement('img');
img.src = dataURL;
img.onload=function(){
ctx.drawImage(img,20,20);
}
// Restote canvas settings
ctx.restore();
<canvas id=canvas width=40 height=40></canvas>
I also met this problem.but after a experiment, I found that Resizing the canvas element will automatically clear all drawings off the canvas!
just try the code below
<canvas id = 'canvas'></canvas>
<script>
var canvas1 = document.getElementById('canvas')
console.log('canvas size',canvas1.width, canvas1.height)
var ctx = canvas1.getContext('2d')
ctx.font = 'Bold 48px Arial'
var f = ctx.font
canvas1.width = 480
var f1 = ctx.font
alert(f === f1) //false
</script>

Categories

Resources