Ionic 3 - canvas draw - how to save and load? - html

I have created an app that allow users to draw and painting any thing
these are the main functions of drawing canvas
ngAfterViewInit(){
this.canvasElement = this.canvas.nativeElement;
this.renderer.setElementAttribute(this.canvasElement, 'width', this.platform.width() + '');
this.renderer.setElementAttribute(this.canvasElement, 'height', this.platform.height() + '');
}
handleStart(ev){
this.lastX = ev.touches[0].pageX;
this.lastY = ev.touches[0].pageY;
}
handleMove(ev){
let ctx = this.canvasElement.getContext('2d');
let currentX = ev.touches[0].pageX;
let currentY = ev.touches[0].pageY;
ctx.beginPath();
ctx.lineJoin = "round";
ctx.moveTo(this.lastX, this.lastY);
ctx.lineTo(currentX, currentY);
ctx.closePath();
ctx.strokeStyle = this.currentColour;
ctx.lineWidth = this.brushSize;
ctx.stroke();
this.lastX = currentX;
this.lastY = currentY;
}
I need to add save and load functions
Only what i need to do is store canvas value in variable to be able to load it.

I got the answer after hours of work
for save i used this way
saveCanvas(){
this.saved = this.canvasElement.toDataURL();
}
for load i used this way
loadCanvas(){
this.clearCanvas();
var canvas = this.canvasElement;
var context = canvas.getContext('2d');
// load image from data url
var imageObj = new Image();
imageObj.onload = function() {
context.drawImage(this, 0, 0);
};
imageObj.src = this.saved;
// make ajax call to get image data url
var request = new XMLHttpRequest();
request.open('GET', 'https://www.html5canvastutorials.com/demos/assets/dataURL.txt', true);
request.onreadystatechange = function() {
// Makes sure the document is ready to parse.
if(request.readyState == 4) {
// Makes sure it's found the file.
if(request.status == 200) {
loadCanvas(request.responseText);
}
}
};
request.send(null);
}

Related

How to show an uploaded image as a canvas background right away?

I'm using typescript to set as a canvas background an image uploaded by users. However, and I guess due to some binding issue, it only works when the user uploads the file two times. How could I wait for the image to load before trying to set it as a canvas background?
I have tried to run the canvas code only when the selectedFile variable is not null (see below) but I'm getting the same error.
<input type="file" (change)="onFileChanged($event)">
<canvas id="canvas"></canvas>
private selectedFile: File = null;
private imgURL: any;
onFileChanged(event){
if(event.target.files.length == 0) return;
this.selectedFile = <File> event.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(this.selectedFile);
reader.onload = (_event) => {this.imgURL = reader.result;}
var canvas : any = document.getElementById("canvas");
var ctxt = canvas.getContext("2d");
var background = new Image();
background.src = this.imgURL;
background.onload = function () {
ctxt.drawImage(background, 0, 0, canvas.width, canvas.height);
};
}
I expect the canvas to show the selected image as a background. However, the first time the user uploads an image, the output is GET http://localhost:4200/undefined 404 (Not Found) Image (async). When the user uploads a different image, the background is updated to the first selected image.
I think all you need to do is put your canvas code inside of the reader.onload callback.
onFileChanged(event) {
if (event.target.files.length == 0) return;
this.selectedFile = < File > event.target.files[0];
var reader = new FileReader();
reader.readAsDataURL(this.selectedFile);
reader.onload = (_event) => {
this.imgURL = reader.result;
var canvas: any = document.getElementById("canvas");
var ctxt = canvas.getContext("2d");
var background = new Image();
background.src = this.imgURL;
background.onload = function() {
ctxt.drawImage(background, 0, 0, canvas.width, canvas.height);
};
}
}
You were likely getting your error because your .onload async callback had not triggered yet and this.imgURL would be undefined when you tried to use it. Now it waits until we have a value for imgURL before tying to use it.

Image not appearing in canvas

I'm trying to load an image on my HTML5 canvas. I'm following a tutorial and I've done everything precisely.
My console prints "image loaded", so I know that it has found the png file. However, nothing shows up on screen.
I have tried resizing the canvas, and trying to make the image appear on different coordinates, to no avail.
<body>
<canvas id="my_canvas"></canvas>
</body>
<script>
var canvas = null;
var context = null;
var basicImage = null;
var setup = function() {
// Set up canvas
canvas = document.getElementById("my_canvas");
context = canvas.getContext('2d'); // lets us modify canvas visuals later
canvas.width = window.innerWidth; // 1200
canvas.height = window.innerHeight; // 720
// Load an image
basicImage = new Image();
basicImage.src = "bb8.png";
basicImage.onload = onImageLoad();
}
var onImageLoad = function() {
console.log("image loaded");
context.drawImage(basicImage, 0, 0);
}
setup();
</script>
Rather than drawing the image on basicImage.onload, try it on window.onload
var setup = function() {
// Set up canvas
canvas = document.getElementById("my_canvas");
context = canvas.getContext('2d'); // lets us modify canvas visuals later
canvas.width = window.innerWidth; // 1200
canvas.height = window.innerHeight; // 720
// Load an image
basicImage = new Image();
basicImage.src = "bb8.png";
}
window.onload = function() {
console.log("image loaded");
context.drawImage(basicImage, 0, 0);
}
setup();

why is my canvas zoom/pan slowing down.

I'm a new programmer just trying to learn how to make webpages. I found a code to zoom and pan canvas elements but when I implemented it into an extJS window It started becoming sluggish. It doesn't become sluggish if the image I render is just a shape, only if It's from a file image. I thought at first I was creating instances of objects over and over but I tried deleting the objects after use and it didn't change anything. Why is my zooming slowing down?
Ext.onReady(function(){
Ext.define("w",{
width: 1000,
height: 750,
extend: "Ext.Window",
html: '<canvas id="myCanvas" width="1000" height="750">'
+ 'alternate content'
+ '</canvas>'
,afterRender: function() {
this.callParent(arguments);
var canvas= document.getElementById("myCanvas");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var stage = new createjs.Stage("myCanvas");
/*function addCircle(r,x,y){
var g=new createjs.Graphics().beginFill("#ff0000").drawCircle(0,0,r);
var s=new createjs.Shape(g)
s.x=x;
s.y=y;
stage.addChild(s);
stage.update();
}*///// If I use this function instead of loading an img there's no slowdown.
function setBG(){
var myImage = new Image();
myImage.src = "dbz.jpg";
myImage.onload = setBG;
var bgrd = new createjs.Bitmap(myImage);
stage.addChild(bgrd);
stage.update();
delete myImage;
delete bgrd;
};
setBG();
//addCircle(40,200,100);
//addCircle(50,400,400);
canvas.addEventListener("mousewheel", MouseWheelHandler, false);
canvas.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
var zoom;
function MouseWheelHandler(e) {
if(Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)))>0)
zoom=1.1;
else
zoom=1/1.1;
stage.regX=stage.mouseX;
stage.regY=stage.mouseY;
stage.x=stage.mouseX;
stage.y=stage.mouseY;
stage.scaleX=stage.scaleY*=zoom;
stage.update();
delete zoom;
}
stage.addEventListener("stagemousedown", function(e) {
var offset={x:stage.x-e.stageX,y:stage.y-e.stageY};
stage.addEventListener("stagemousemove",function(ev) {
stage.x = ev.stageX+offset.x;
stage.y = ev.stageY+offset.y;
stage.update();
delete offset;
});
stage.addEventListener("stagemouseup", function(){
stage.removeAllEventListeners("stagemousemove");
});
});
} //end aferrender
}); //end define
Ext.create("w", {
autoShow: true });
}); //end onready
It looks like you are infinitely re-loading the BG image. After your BG image finishes loading, your onload function callback just makes it call getBG again which will just repeat the same process forever.
function setBG() {
...
myImage.onload = setBG;
...
}
I'm not sure exactly what you expect by doing this.
You really shouldn't need to delete the image. Off the top of my head, this is how I would generally load an image for use in canvas, (based on how your train of thought is working).
function setBG(){
var myImage = new Image();
myImage.src = "dbz.jpg";
myImage.onload = function(){
var bgrd = new createjs.Bitmap(this);
stage.addChild(bgrd);
stage.update();
}
};
setBG();

Swapping between layers in kineticJS

I'm trying to treat layers as pages -- i.e. I draw on one page, then turn the page and draw on another, each time storing the previous page in case the user goes back to it.
In my mind this translates as:
Create current_layer global pointer.
Each time newPage() is called, store the old layer in an array, and overwrite the pointer
layer_array.push(current_layer); //store old layer
current_layer = new Kinetic.Layer(); //overwrite with a new
New objects are then added to the current_layer which binds them to the layer, whether they are drawn or not. (e.g. current_layer.add(myCircle) )
Retrieving a page is simply updating the pointer to the requesting layer in the array, and redrawing the page. All the child nodes attached to the layer will also be drawn too
current_layer = layer_array[num-1]; //num is Page 2 e.g
current_layer.draw()
However nothing is happening! I can create new pages, and store them appropriately - but I cannot retrieve them again...
Here's my full code (my browser is having problems using jsfiddle):
<html>
<head>
<script src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.0.min.js"></script>
<script>
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
layer_array.push(current_page);
// stage.remove(current_page);
//Nope, not working.
stage.removeChildren();
//Works, but I think it unbinds all objects
// from their specific layers...
// stage.draw()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
}
function gotoPage(num){
stage.removeChildren()
stage.draw()
num = num-1;
if(num >= 0) {
current_page = layer_array[num];
console.log("Now on page"+(num+1));
stage.add(current_page);
stage.draw();
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16,
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}
</script>
</head>
<body>
<div id="container"></div>
<button onclick="addCircletoCurrentPage()" >click</button>
<button onclick="newPage()" >new</button>
<button onclick="gotoPage(1)" >page1</button>
<button onclick="gotoPage(2)" >page2</button>
<button onclick="gotoPage(3)" >page3</button>
</body>
</html>
This was a fun problem. I think this fixes your troubles: http://jsfiddle.net/LRNHk/3/
Basically, you shouldn't remove() or removeChildren() as you risk de-referencing them.
Instead you should use:
layer.hide(); and layer.show();
this way, you keep all things equal and you get speedy draw performance.
So your go to page function should be like this:
function gotoPage(num){
for(var i=0; i<layer_array.length; i++) {
layer_array[i].hide();
}
layer_array[num].show();
console.log("Currently on page:"+(num));
console.log("Current layer: " + layer_array[num].getName());
stage.draw();
}
I also modified your other functions, which you can see in the jsfiddle.
Okay I changed my approach and instead of swapping layers (100x easier and makes more sense), I instead opted for serializing the entire stage and loading it back.
It works, but it really shouldn't have to be like this dammit
//Global
var stage; //canvas
var layer_array = [];
var current_page; //pointer to current layer
var page_num = 0;
window.onload = function() {
stage = new Kinetic.Stage({
container: 'container',
width: 400,
height: 400
});
//Add initial page to stage to draw on
newPage()
};
//--- Functions ----//
function newPage(){
if(!current_page){
console.log("current page undefined");
} else {
savePage(page_num)
stage.removeChildren()
console.log("Stored layer and removed it from stage");
}
current_page = new Kinetic.Layer();
console.log("Currently on page:"+(layer_array.length+1));
stage.add(current_page);
stage.draw();
page_num ++;
}
function savePage(num){
if( (num-1) >=0){
var store = stage.toJSON();
layer_array[num-1] = store;
console.log("Stored page:"+num)
}
}
function gotoPage(num){
savePage(page_num);
stage.removeChildren()
if(num-1 >= 0) {
var load = layer_array[num-1];
document.getElementById('container').innerHTML = ""; //blank
stage = Kinetic.Node.create(load, 'container');
var images = stage.get(".image");
for(i=0;i<images.length;i++)
{
//function to induce scope
(function() {
var image = images[i];
var imageObj = new Image();
imageObj.onload = function() {
image.setImage(imageObj);
current_page.draw();
};
imageObj.src = image.attrs.src;
})();
}
stage.draw();
page_num =num //update page
}
}
function addCircletoCurrentPage()
{
var rand = Math.floor(3+(Math.random()*10));
var obj = new Kinetic.Circle({
x: rand*16, y: rand*16, name: "image",
radius: rand,
fill: 'red'
})
var imagelayer = current_page;
imagelayer.add(obj);
imagelayer.draw();
}

Moving the start position of canvas pattern

I have a code:
function draw(ctx) {
// create new image object to use as pattern
var img = new Image();
img.onload = function(){
// create pattern
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0,0,150,150);
}
img.src = 'images/wallpaper.png?' + new Date().getTime();
}
How can i move the start position of pattern image?
In response to the accepted answer: rather than undo the offset, I would use save() & restore() to avoid potential problems:
ctx.save();
ctx.translate(offset_x, offset_y);
ctx.fillRect(-offset_x, -offset_y, fill_x, fill_y);
ctx.restore();
You can achieve this by translating the canvas, drawing on it, and then translating it back to where you started:
function draw(ctx) {
// create new image object to use as pattern
var img = new Image();
img.onload = function(){
// create pattern
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
// offset vars
var offset_x = 60;
var offset_y = 75;
var fill_x = 500; // could be canvas.width
var fill_y = 500; // could be canvas.height
// offset
ctx.translate(offset_x, offset_y);
// draw
ctx.fillRect(-offset_x, -offset_y, fill_x, fill_y);
// undo offset
ctx.translate(-offset_x, -offset_y);
}
img.src = 'images/wallpaper.png?' + new Date().getTime();
}
More general, complex transforms of the pattern alone are easily done. The trick is to do them immediately before the call to fill() or stroke().
function draw(ctx) {
// create new image object to use as pattern
var img = new Image();
img.onload = function(){
// create pattern
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
ctx.beginPath();
ctx.rect(0, 0, 150, 150);
ctx.translate(-33, -33);
ctx.fill();
}
img.src = 'images/wallpaper.png?' + new Date().getTime();
}