Canvas rendering from component is not working on Svelte - javascript

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

Related

document.createElement does not work as expected in componentDidMount in React?

I am trying to create a canvas element on the fly, in order to execute .toDataURL() method after I've done some drawing. But the document.createElement does not have the expected behavior.
When I try this:
componentDidMount() {
var canvas = document.createElement('canvas');
canvas.height = 100;
canvas.width = 200;
console.log(canvas);
}
I get in the console:
<canvas height="0" width="0"></canvas>
But using componentDidUpdate method works fine.
componentDidUpdate() {
var canvas = document.createElement('canvas');
canvas.height = 100;
canvas.width = 200;
console.log(canvas);
}
<canvas height="100" width="200"></canvas>
What exactly is happening here ?
Please any answers concerning the right way in React using either ref or applying <canvas /> element to DOM won't help me, as I am particularly trying to understand why there is this difference between componentDidMount and componentDidUpdate in React.
Ref is the proper way to target an element in React, and The proper way to creating an element in React returns them using jsx.
This example shows you how to make canvas:
const App = () => {
const drawCanvas = (ref) => {
if (ref) {
ref.width = 200;
ref.height = 100;
const ctx = ref.getContext("2d");
ctx.clearRect(0, 0, 200, 100);
ctx.fillStyle = "grey";
ctx.fillRect(0, 0, 200, 100);
}
};
return <canvas ref={drawCanvas}/>
};
ReactDOM.render(<App />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Responsive full width canvas in sveltejs

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

Save State of Canvas in React

I am implementing Draw function, to draw a line in canvas. The Draw function is activated on the first double click and on mouse move the line is being drawn and on the second double click, the draw function is deactivated.
When I double click the third time the previously drawn line is disappearing. My requirement is I want to retain all of the lines. How do I do that?
Below is my draw function:
handleMouseMove(event){
if(this.state.isDouble)
{
this.Draw(event)
}
else{
}
}
Draw(event){
x2=event.offsetX
y2=event.offsetY
const canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.lineWidth = 3;
//Deleting The State
ctx.canvas.width = ctx.canvas.width;
console.log("First" + this.state.previousPointX,this.state.previousPointY)
console.log("Second" + x2,y2)
xtemp=x2
ytemp=this.state.previousPointY
console.log("Temp" + xtemp,ytemp)
ctx.beginPath()
ctx.moveTo(xtemp,ytemp);
ctx.lineTo(x2,y2);
ctx.stroke();
ctx.moveTo(xtemp,ytemp);
ctx.lineTo(this.state.previousPointX,this.state.previousPointY);
ctx.strokeStyle = "black";
ctx.stroke();
}
I have read here that you can save the canvas by converting it to json
for Redux store, I have tested using state for my Fabric canvas instance, its a 3rd party library that is a wrapper on a HTML canvas DOM element.
import React, { useEffect, useState } from 'react';
import './WallToDecorate.css';
import { fabric } from 'fabric';
const WallToDecorate = ({templateList=[]}) => {
const [wallCanvas, setCanvas] = useState();
useEffect(() => {
const canvas = new fabric.Canvas('fabric-canvas-to-decorate', {
width: rect.width,
height: rect.height
});
canvas.setBackgroundImage(
originalWallImage.src,
canvas.renderAll.bind(canvas),
{
backgroundImageOpacity: 1,
backgroundImageStretch: false,
top: top,
left: left,
}
);
setCanvas(canvas);
}, []);
const addImageToCanvas = (event) => {
fabric.util.loadImage(
event.target.src,
function (img) {
var fab_image = new fabric.Image(img, {
});
wallCanvas.add(fab_image);
wallCanvas.renderAll();
},
{ left: 100, top: 100, angle: 0, opacity: 1 }
);
};
return (
<>
<div id="select-image-dimensions" className="placeholder canvas-size">
<canvas id="fabric-canvas-to-decorate" />
</div>
</>
);
}
export default WallToDecorate;
In my case the wallCanvas.renderAll() refreshes the canvas element.

How to Stop multiply a canvas line when the windows is resizing?

I have a function that draw a CANVAS line and make this get the same coordinates of a <div> by using offsetLeft and move its searching the same position of the <div>. It is working good.
drawCanvas() {
const c = document.getElementById("canvas");
const lineH = c.getContext("2d");
c.width = window.innerWidth;
c.height = window.innerHeight;
const positionCanvas = () => {
const divPosition = document.querySelector('.myDiv').offsetLeft
lineV.fillStyle = "grey";
lineV.fillRect(divPosition , 0, 2, window.innerHeight);
lineV.fill();
}
positionCanvas()
window.onresize = () => {
lineV.height = window.innerHeight;
positionCanvas()
}
The problem is I don't know how avoid the default CANVAS behavior that duplicates many times the line when I resize the windows. How do I solve it? Thank you
The answer is:
const positionCanvas = () => {
lineV.clearRect(0, 0, c.width, c.height); //<-- new line added in the code
//... rest of the code ommited

Take Keyboard Input Into Ember Component

I've created an ember component with a rectangular block inside a green canvas.
What I'm having trouble with is adding a keyboard-input command for A S D W to move the rectangle around the canvas. It's easy enough to do in regular javascript or jquery but inside the component model I'm a bit lost. Any help regarding the function would be very useful.
Linked here is an ember javascript bin: http://emberjs.jsbin.com/miyatoti/1/edit
Here is my present code of the component.
App.BoxGameComponent = Em.Component.extend({
tagName:'canvas',
width: 325,
height: 280,
refresh:30,
attributeBindings:['width', 'height'],
stars:null,
on:false,
build: function(){
var canvas = this.$()[0],
ctx = canvas.getContext('2d'),
shippos = [150, 120],
height = this.get('height'),
width = this.get('width');
this.set('shippos', shippos);
this.set('ctx', ctx);
this.set('on', true);
}.on('didInsertElement'),
kill: function(){
this.set('on', false);
}.on('willDestroyElement'),
clear: function () {
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width');
ctx.fillStyle = 'green';
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.rect(0, 0, width, height);
ctx.closePath();
ctx.fill();
},
box: function () {
var that = this;
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width'),
shippos = this.get('shippos');
var posx = shippos[0],
posy = shippos[1];
ctx.rect(posx,posy,50,50);
ctx.stroke();
},
game: function(){
if(this.get('on')){
this.loop();
}
}.observes('on'),
loop: function () {
var refreshRate = this.get('refresh');
this.clear();
this.box();
if(this.get('on')){
Em.run.later(this, this.loop, refreshRate);
}
}
});
If anyone can help I've been slamming my brain at this for hours.
Hooking up keyup a canvas element is a bit trickier since the canvas doesn't get focus. So you just hook up to the window (and then destroy it later).
$(window).on('keyup', {self:this}, this.handleKeyUp );
http://emberjs.jsbin.com/miyatoti/2/edit

Categories

Resources