This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 6 years ago.
First of all, i know there are millions of questions like this. But i could not any that helps me.
I'm calling the draw method after all images loaded. As you can see, when i try to access a variable (loadedImages, this) inside the draw method i get undefined.
Why is this happening and how can i get that variables?
var canvas = $('#area')[0],
context = canvas.getContext('2d');
function Character() {
return {
images: ["hair.png", "head.png", "mouth.png"],
loadedImages: {},
init: function() {
this.loadImages();
},
loadImages: function() {
var loaded = 0,
imagesLength = this.images.length,
draw = this.draw;
for(var i = 0; i <= imagesLength - 1; i++) {
var image = new Image(),
bodyPart = this.images[i];
image.onload = function() {
loaded++;
if(loaded == imagesLength) {
draw();
}
};
image.src = 'characters/Canser/' + bodyPart;
this.loadedImages[bodyPart.split(".")[0]] = image;
}
},
draw: function() {
console.log(this); // undefined???
}
};
}
var canser = new Character();
canser.init();
Store this in that and use that.draw()
var canvas = $('#area')[0],
context = canvas.getContext('2d');
function Character() {
return {
images: ["hair.png", "head.png", "mouth.png"],
loadedImages: {},
init: function() {
this.loadImages();
},
loadImages: function() {
var loaded = 0,
that = this,
imagesLength = this.images.length,
draw = this.draw;
for(var i = 0; i <= imagesLength - 1; i++) {
var image = new Image(),
bodyPart = this.images[i];
image.onload = function() {
loaded++;
if(loaded == imagesLength) {
that.draw();
}
};
image.src = 'characters/Canser/' + bodyPart;
this.loadedImages[bodyPart.split(".")[0]] = image;
}
},
draw: function() {
console.log(this); // undefined???
}
};
}
Related
I am erasing the canvas drawing 1 time then Undo works fine, but when I erase more than 1 time then the Undo work as a line stroke, it works for the last step to erase but other are working as line stroke please see below code:
function Sketchpad(config) {
// Enforces the context for all functions
for (var key in this.constructor.prototype) {
this[key] = this[key].bind(this);
}
// Warn the user if no DOM element was selected
if (!config.hasOwnProperty('element')) {
console.error('SKETCHPAD ERROR: No element selected');
return;
}
this.element = config.element;
// Width can be defined on the HTML or programatically
this._width = config.width || $(this.element).attr('data-width') || 0;
this._height = config.height || $(this.element).attr('data-height') || 0;
// Pen attributes
this.color = config.color || $(this.element).attr('data-color') || '#000000';
this.penSize = config.penSize || $(this.element).attr('data-penSize') || 5;
// ReadOnly sketchpads may not be modified
this.readOnly = config.readOnly ||
$(this.element).attr('data-readOnly') ||
false;
if (!this.readOnly) {
$(this.element).css({cursor: 'crosshair'});
}
// Stroke control variables
this.strokes = config.strokes || [];
console.log(this.strokes)
this._currentStroke = {
color: null,
size: null,
lines: [],
};
// Undo History
this.undoHistory = (!bErasing) ? (config.undoHistory || []) : [];
// Animation function calls
this.animateIds = [];
// Set sketching state
this._sketching = false;
// Setup canvas sketching listeners
this.reset();
}
//
// Private API
//
Sketchpad.prototype._cursorPosition = function(event) {
return {
x: event.pageX - $(this.canvas).offset().left,
y: event.pageY - $(this.canvas).offset().top,
};
};
Sketchpad.prototype._draw = function(start, end, color, size) {
this._stroke(start, end, color, size, 'source-over');
};
Sketchpad.prototype._erase = function(start, end, color, size) {
this._stroke(start, end, color, size, 'destination-out');
};
Sketchpad.prototype._stroke = function(start, end, color, size) {
this.context.save();
if(bErasing){
this.context.globalCompositeOperation = "destination-out";
}else{
this.context.globalCompositeOperation = "source-over";
}
// if(size === 7 || size === 13){
// console.log(size)
// this.context.lineJoin = 'square';
// this.context.lineCap = 'square';
// }
// else{
this.context.lineJoin = 'round';
this.context.lineCap = 'round';
// }
this.context.strokeStyle = color;
this.context.lineWidth = size;
// switch(size){
// case 2:
// this.context.globalAlpha = 1;
// break;
// case 7:
// this.context.globalAlpha = 0.7;
// break;
// case 13:
// this.context.globalAlpha = 0.4;
// break;
// default:
// this.context.globalAlpha = 0.2;
// }
this.context.beginPath();
this.context.moveTo(start.x, start.y);
this.context.lineTo(end.x, end.y);
this.context.closePath();
this.context.stroke();
this.context.restore();
};
//
// Callback Handlers
//
Sketchpad.prototype._mouseDown = function(event) {
this._lastPosition = this._cursorPosition(event);
this._currentStroke.color = this.color;
this._currentStroke.size = this.penSize;
this._currentStroke.lines = [];
// console.log(this._currentStroke.lines)
this._sketching = true;
this.canvas.addEventListener('mousemove', this._mouseMove);
};
Sketchpad.prototype._mouseUp = function(event) {
if (this._sketching) {
this.strokes.push($.extend(true, {}, this._currentStroke));
this._sketching = false;
}
this.canvas.removeEventListener('mousemove', this._mouseMove);
};
Sketchpad.prototype._mouseMove = function(event) {
var currentPosition = this._cursorPosition(event);
this._draw(this._lastPosition, currentPosition, this.color, this.penSize);
this._currentStroke.lines.push({
start: $.extend(true, {}, this._lastPosition),
end: $.extend(true, {}, currentPosition),
});
this._lastPosition = currentPosition;
};
Sketchpad.prototype._touchStart = function(event) {
event.preventDefault();
if (this._sketching) {
return;
}
this._lastPosition = this._cursorPosition(event.changedTouches[0]);
this._currentStroke.color = this.color;
this._currentStroke.size = this.penSize;
this._currentStroke.lines = [];
this._sketching = true;
this.canvas.addEventListener('touchmove', this._touchMove, false);
};
Sketchpad.prototype._touchEnd = function(event) {
event.preventDefault();
if (this._sketching) {
this.strokes.push($.extend(true, {}, this._currentStroke));
this._sketching = false;
}
this.canvas.removeEventListener('touchmove', this._touchMove);
};
Sketchpad.prototype._touchCancel = function(event) {
event.preventDefault();
if (this._sketching) {
this.strokes.push($.extend(true, {}, this._currentStroke));
this._sketching = false;
}
this.canvas.removeEventListener('touchmove', this._touchMove);
};
Sketchpad.prototype._touchLeave = function(event) {
event.preventDefault();
if (this._sketching) {
this.strokes.push($.extend(true, {}, this._currentStroke));
this._sketching = false;
}
this.canvas.removeEventListener('touchmove', this._touchMove);
};
Sketchpad.prototype._touchMove = function(event) {
event.preventDefault();
var currentPosition = this._cursorPosition(event.changedTouches[0]);
this._draw(this._lastPosition, currentPosition, this.color, this.penSize);
this._currentStroke.lines.push({
start: $.extend(true, {}, this._lastPosition),
end: $.extend(true, {}, currentPosition),
});
this._lastPosition = currentPosition;
};
//
// Public API
//
Sketchpad.prototype.reset = function() {
// Set attributes
this.canvas = $(this.element)[0];
this.canvas.width = this._width;
this.canvas.height = this._height;
this.context = this.canvas.getContext('2d');
// Setup event listeners
this.redraw(this.strokes);
if (this.readOnly) {
return;
}
// Mouse
this.canvas.addEventListener('mousedown', this._mouseDown);
this.canvas.addEventListener('mouseout', this._mouseUp);
this.canvas.addEventListener('mouseup', this._mouseUp);
// Touch
this.canvas.addEventListener('touchstart', this._touchStart);
this.canvas.addEventListener('touchend', this._touchEnd);
this.canvas.addEventListener('touchcancel', this._touchCancel);
this.canvas.addEventListener('touchleave', this._touchLeave);
};
Sketchpad.prototype.drawStroke = function(stroke) {
for (var j = 0; j < stroke.lines.length; j++) {
var line = stroke.lines[j];
this._draw(line.start, line.end, stroke.color, stroke.size);
}
};
Sketchpad.prototype.erase = function() {
// this._erase(line.start, line.end, stroke.color, stroke.size);
};
Sketchpad.prototype.redraw = function(strokes) {
for (var i = 0; i < strokes.length; i++) {
this.drawStroke(strokes[i]);
}
};
Sketchpad.prototype.toObject = function() {
return {
width: this.canvas.width,
height: this.canvas.height,
strokes: this.strokes,
undoHistory: this.undoHistory,
};
};
Sketchpad.prototype.toJSON = function() {
return JSON.stringify(this.toObject());
};
Sketchpad.prototype.animate = function(ms, loop, loopDelay) {
this.clear();
var delay = ms;
var callback = null;
for (var i = 0; i < this.strokes.length; i++) {
var stroke = this.strokes[i];
for (var j = 0; j < stroke.lines.length; j++) {
var line = stroke.lines[j];
callback = this._draw.bind(this, line.start, line.end,
stroke.color, stroke.size);
this.animateIds.push(setTimeout(callback, delay));
delay += ms;
}
}
if (loop) {
loopDelay = loopDelay || 0;
callback = this.animate.bind(this, ms, loop, loopDelay);
this.animateIds.push(setTimeout(callback, delay + loopDelay));
}
};
Sketchpad.prototype.cancelAnimation = function() {
for (var i = 0; i < this.animateIds.length; i++) {
clearTimeout(this.animateIds[i]);
}
};
Sketchpad.prototype.clear = function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
};
Sketchpad.prototype.undo = function() {
this.clear();
var stroke = this.strokes.pop();
if (stroke) {
this.undoHistory.push(stroke);
this.redraw(this.strokes);
}
};
Sketchpad.prototype.redo = function() {
var stroke = this.undoHistory.pop();
if (stroke) {
this.strokes.push(stroke);
this.drawStroke(stroke);
}
};
and also having issue while trying the change the texture of brushes according to the pen size.
eraser is working fine and undo is also working fine the only issue is while we erase more than once, then try to undo then last erase is return to real drawing, but other previous works like as the line stroke and display the drawing as per eraser mouse move in canvas.
This is my my function to call above methods:
var x = "#CB7342",
y = 2;
var bErasing = false;
var canvas, canvasWidth, canvasHeight;
var ctx;
var lastPt=null;
var pathsry = [];
var points = [];
var state;
var colour;
// past states
function ySize(size) {
y = size;
sketchpad.penSize = y;
}
var sketchpad;
function undo(){
bErasing = false;
sketchpad.undo();
if($('#erase').hasClass('active')){
bErasing = true;
}
}
function init() {
canvas = document.getElementById('sheet');
ctx = canvas.getContext('2d');
var canvasOffset = $('#sheet').offset();
var parent = canvas.parentNode;
canvas.width = parent.clientWidth;
canvas.height = parent.clientHeight;
// alert(bErasing)
sketchpad = new Sketchpad({
element: '#sheet',
width: canvas.width,
height: canvas.height,
color: null,
penSize: 2,
globalAlpha: 0.1
});
}
$(document).on('click', "#erase", function () {
bErasing = true;
});
I have been trying to draw a tile-like map to the canvas, although if i try to draw the same sprite twice, it will only render the final call of drawImage. Here is my code if it helps :
window.onload = function(){
function get(id){
return document.getElementById(id);
}
var canvas = get("canvas");
ctx = canvas.getContext("2d");
canvas.width = 160;
canvas.height = 160;
grass = new Image();
water = new Image();
grass.src = "res/Grass.png";
water.src = "res/Water.png";
path = {
draw: function(image,X,Y){
X = (X*32)-32;
Y = (Y*32)-32;
image.onload = function(){
ctx.drawImage(image,X,Y);
}
},
}
path.draw(water,2,2);
path.draw(grass,1,2);
path.draw(grass,1,1);
path.draw(water,2,1);
}
You're overwriting onload on the image next time you call it.
Think of it this way:
var image = {
onload: null
};
// Wait for the image to "load"
// Remember, onload is asynchronous so it won't be called until the rest of
// your code is done and the image is loaded
setTimeout(function() {
image.onload();
}, 500);
image.onload = function() {
console.log('I will never be called');
};
image.onload = function() {
console.log('I overwrite the previous one');
};
Instead, you might try waiting for your images to load then do the rest of your logic.
var numOfImagesLoading = 0;
var firstImage = new Image();
var secondImage = new Image();
// This is called when an image finishes loading
function onLoad() {
numOfImagesLoading -= 1;
if (numOfImagesLoading === 0) {
doStuff();
}
}
function doStuff() {
// This is where you can use your images
document.body.appendChild(firstImage);
document.body.appendChild(secondImage);
}
firstImage.src = 'http://placehold.it/100x100';
firstImage.onload = onLoad;
numOfImagesLoading += 1;
secondImage.src = 'http://placehold.it/200x200';
secondImage.onload = onLoad;
numOfImagesLoading += 1;
I am trying to make really simple image loader for my game but I can't find out why this isn't working.. Here is my code:
window.onload = function() {
var canvas = document.getElementById('gameCanvas');
var ctx = canvas.getContext('2d');
var images = [];
function loadImages(imageFiles) {
var loadedImages = [];
for(var i = 0; i < imageFiles.length; i++) {
var image = new Image();
image.onload = function() {
alert("Loaded");
}
image.src = imageFiles[i];
loadedImages[i] = image;
}
return loadedImages;
}
function init() {
images = loadImages(['img/1.png', 'img/2.png']);
main();
}
function main() {
ctx.drawImage(images[1], 0,0);
}
init();
}
All I see is blank canvas without an image.
Use Promise, The Promise object is used for deferred and asynchronous computations. A Promise represents an operation that hasn't completed yet, but is expected in the future.
Promise.all(), The Promise.all(iterable) method returns a promise that resolves when all of the promises in the iterable argument have resolved.
function loadImages(imageFiles) {
var promiseArr = [];
for (var i = 0; i < imageFiles.length; i++) {
var eachPromise = new Promise(function(resolve, reject) {
var image = new Image();
image.onload = function() {
alert('Loaded!');
resolve();
}
image.src = imageFiles[i];
});
promiseArr.push(eachPromise);
}
return promiseArr;
}
function init() {
var AllImages = ['https://www.google.co.in/logos/doodles/2016/earth-day-2016-5741289212477440.2-5643440998055936-ror.jpg', 'https://www.google.co.in/logos/doodles/2016/earth-day-2016-5741289212477440.3-5700735861784576-ror.jpg'];
var allPromises = loadImages(AllImages);
Promise.all(allPromises).then(function() {
alert('All Loaded');
main();
});
}
function main() {}
init();
try replacing
image.onload = function() {
alert("Loaded");
}
with
if(i==1){
image.onload = function() {
main();
}}
when the second image is loaded, your function is called
then edit the code to suit your needs
I will edit more in a while
Images are loaded asynchronously so when you try to read the file in main, the image still is loading. So you need to wait until all the images are loaded.
window.addEventistener("load", function() {
var canvas = document.getElementById('gameCanvas');
var ctx = canvas.getContext('2d');
var images = [];
function loadImages(imageFiles, completeFnc) {
var loadedImages = [],
loadedCnt = 0;
for(var i = 0; i < imageFiles.length; i++) {
var image = new Image();
image.onload = function() {
loadedCnt++; //increment the count
if(loadedCnt==imageFiles.length) { //if count equals total, fire off callback
completeFnc();
}
};
image.onerror = function () {
alert("There was a problem loading the images");
};
image.src = imageFiles[i];
loadedImages[i] = image;
}
return loadedImages;
}
function init() {
images = loadImages(['img/1.png', 'img/2.png'], main);
}
function main() {
ctx.drawImage(images[1], 0,0);
}
init();
});
This should work better
var images = ['img/1.png', 'img/2.png'], loadedImages = [], canvas,ctx, idx=0:
function main() {
ctx.drawImage(images[1], 0, 0);
}
function loadImages() {
if (loadedImages.length == images.length) {
main();
return;
}
var image = new Image();
image.onload = function() {
loadedImages.push(this);
loadImages();
idx++;
}
image.src = imageFiles[idx];
}
function init() {
canvas = document.getElementById('gameCanvas');
ctx = canvas.getContext('2d');
loadImages();
}
window.onload = function() {
init();
}
You need to push items into an array. Replace
loadedImages[i] = image;
with
loadedImages.push(image);
I want to write Bitmap eraser on easelJS. I think, i'm on half of my road to do this....
Here is demo:
http://jsfiddle.net/bordeux/g2Lwvsuv/2/
and my code:
var example = function() {
this.constructor.apply(this, arguments);
};
example.prototype.constructor = function() {
this.$canvas = $("canvas");
this.container = {};
this.objects = {};
this.resizeEvents = [];
this.prepareCanvas();
this.refreshSize();
this.bindEvents();
};
example.prototype.bindEvents = function() {
var self = this;
$(window).resize(function(){
self.refreshSize();
});
};
example.prototype.refreshSize = function() {
this.stage.canvas.width = window.innerWidth;
this.stage.canvas.height = window.innerHeight;
this.resizeEvents.forEach(function(item) {
item();
});
};
example.prototype.getObject = function(name) {
return this.objects[name];
};
example.prototype.setObject = function(name, obj) {
this.objects[name] = obj;
return obj;
};
example.prototype.addResizeEvent = function(foo) {
this.resizeEvents.push(foo);
return this;
};
example.prototype.initCursor = function() {
var self = this;
var size = 50;
/**
* Circle cursor
*/
var circle = new createjs.Shape();
circle.graphics.setStrokeStyle(2).beginStroke("#171717").drawCircle(0, 0, size);
/**
* Hit area
*/
var hit = new createjs.Shape();
hit.graphics.beginFill("#000").drawCircle(0, 0, size);
circle.hitArea = hit;
var container = this.getContainer("cursor");
container.addChild(circle);
this.stage.on("stagemousemove", function(evt) {
circle.setTransform(evt.stageX, evt.stageY);
this.setChildIndex( container, this.getNumChildren()-1);
});
var drawing = this.setObject("image.mask", new createjs.Shape());
drawing.visible = false;
var lastPoint = new createjs.Point();
container.addChild(drawing);
circle.on("mousedown", function(event) {
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
});
circle.on("pressmove", function(event) {
drawing.graphics
.setStrokeStyle(100, "round", "round")
.beginStroke("rgba(255, 255, 255, 0.2)")
.beginRadialGradientFill(
["rgba(0,0,0,0)", "rgba(0,0,0,1)"],
[0.1, 1],
50, 50, 0,
50, 50, 50
)
.moveTo(lastPoint.x, lastPoint.y)
.lt(event.stageX, event.stageY);
lastPoint.x = event.stageX;
lastPoint.y = event.stageY;
self.updateCache();
});
circle.on("pressup", function(event) {
self.updateCache();
});
};
example.prototype.updateCache = function(update) {
(update === undefined) && (update = false);
var drawingCanvas = this.getObject("image.mask");
var image = this.getObject("image");
if (update) {
drawingCanvas.updateCache();
} else {
drawingCanvas.cache(0, 0, image.image.naturalWidth, image.image.naturalHeight);
}
image.filters = [
new createjs.AlphaMaskFilter(drawingCanvas.cacheCanvas)
];
if (update) {
image.updateCache(0, 0, image.image.naturalWidth, image.image.naturalHeight);
} else {
image.cache(0, 0, image.image.naturalWidth, image.image.naturalHeight);
}
};
example.prototype.prepareCanvas = function() {
this.stage = new createjs.Stage(this.$canvas[0]);
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", this.stage);
this.initCursor();
this.loadImage();
};
/**
* Get container. If not exist, this function create new one
* #param {String} name
* #returns {createjs.Container}
*/
example.prototype.getContainer = function(name) {
if(this.container[name]){
return this.container[name];
}
this.container[name] = new createjs.Container();
this.stage.addChild(this.container[name]);
return this.container[name];
};
example.prototype.loadImage = function() {
var self = this;
var url = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/aa/Logo_Google_2013_Official.svg/1280px-Logo_Google_2013_Official.svg.png";
var background = this.setObject("image.background", new createjs.Shape());
this.getContainer("image").addChild(background);
var image = null;
return utils.image(url).then(function(img){
image = img;
self.getContainer("image").addChild(self.setObject(
"image",
new createjs.Bitmap(img)
));
self.updateCache(false);
return utils.image("http://i.imgur.com/JKaeYwv.png");
}).then(function(imgBackground){
background
.graphics
.beginBitmapFill(imgBackground,'repeat')
.drawRect(0,0, image.naturalWidth, image.naturalHeight);
});
};
utils = {};
utils.image = function(url){
var deferred = Q.defer();
//deferred.resolve(text);
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
deferred.resolve(this);
};
img.onerror = function(){
deferred.reject(arguments);
};
img.src = url;
return deferred.promise;
};
$(function(){
var test = new example();
});
But when i draw something on my canvas, my path of draw showing image behind mask... This is opposed result what i want.
I want remove part of image (like eraser in Photoshop). I don't know what i should do next...
Thanks
You can use compositeOperation in conjunction with layers or caching to "erase" content. For example, if you use updateCache("source-over"), that will draw the current visual content over the existing cache (normal compositing). However, if you use updateCache("destination-out"), that will essentially punch out the current content from the existing cache, which gives you an eraser effect.
Here's a simple example of this in action: http://jsfiddle.net/17xec9y5/4
Before your read this, my first account was blocked because i asked bad questions.. So please dont vote negative but say what i am doing wrong
Sooo I have this script:
var img1 = new Image()
img1.src="image1.jpg"
var img2 = new Image()
img2.src="image2.jpg"
var img3 = new Image()
img3.src="image3.jpg"
function Canvas() {
var ctx = document.getElementById('slider').getContext('2d');
var pic=1
function slider() {
this.draw = function() {
if(pic<4){pic++}
else{
pic=1
}
var img = "img"+pic
ctx.drawImage(img,0,0,ctx.canvas.width,ctx.canvas.height)
}
}
var slider = new slider();
function draw() {
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.save();
//draw
slider.draw();
//draw
ctx.restore();
}
var animateInterval = setInterval(draw,100)
}
window.addEventListener('load', function(event) {
Canvas();
});
I am trying to draw the image 1 or 2 or 3 on my canvas. But I also have the var pic wich has the number. So i tried ctx.drawimage(img+pic,0,0) or var img = "img"+pic and draw it then. But it doesnt work for me.
I hope you accept my question and can answer it, THNX
Use an array instead
var img = [];
/* you should use a for loop here */
for (var i = 0; i < 3; i++) {
img[i] = new Image();
img[i].src = "image" + (i+1) ".jpg";
}
and later you refer the right image with img[pic]. Be only sure to use an index between 0 and img.length - 1
Don't use separate variables, use an array.
var images = [ new Image(), new Image(), new Image() ];
for (var i = 0; i < images.length; i++) {
images[i].src = "image" + (i+1) + ".jpg";
}
Then refer to the array in the Slider() function:
var pic = 0;
function slider() {
this.draw = function() {
pic = (pic + 1) % images.length;
var img = images[pic];
ctx.drawImage(img,0,0,ctx.canvas.width,ctx.canvas.height)
}
}
The error seems to be that you refer to the string "img1" and not the object img1. Try use an array instead.
Set the following:
...
var pic=0;
var img=[img1,img2,img3];
function slider() {
this.draw = function() {
if(pic<3){pic++}
else{
pic=0;
}
ctx.drawImage(img[pic],0,0,ctx.canvas.width,ctx.canvas.height)
}
}
...