Creating a scratch card in EaselJS - javascript

I've been trying to develop a scratch card in EaselJS.
So far, I've managed to get a Shape instance above a Bitmap one and enabled erasing it with click and drag events, so the image below becomes visible.
I've used the updateCache() with the compositeOperation approach and it was easy enough, but here is my issue:
How can I find out how much the user has already erased from the Shape instance, so I can setup a callback function when, say, 90% of the image below is visible?
Here is a functioning example of what I'm pursuing: http://codecanyon.net/item/html5-scratch-card/full_screen_preview/8721110?ref=jqueryrain&ref=jqueryrain&clickthrough_id=471288428&redirect_back=true
This is my code so far:
function Lottery(stageId) {
this.Stage_constructor(stageId);
var self = this;
var isDrawing = false;
var x, y;
this.autoClear = true;
this.enableMouseOver();
self.on("stagemousedown", startDrawing);
self.on("stagemouseup", stopDrawing);
self.on("stagemousemove", draw);
var rectWidth = self.canvas.width;
var rectHeight = self.canvas.height;
// Image
var background = new createjs.Bitmap("http://www.taxjusticeblog.org/lottery.jpg");
self.addChild(background);
// Layer above image
var overlay = new createjs.Shape();
overlay.graphics
.f("#55BB55")
.r(0, 0, rectWidth, rectHeight);
self.addChild(overlay);
overlay.cache(0, 0, self.canvas.width, self.canvas.height);
// Cursor
self.brush = new createjs.Shape();
self.brush.graphics
.f("#DD1111")
.dc(0, 0, 5);
self.brush.cache(-10, -10, 25, 25);
self.cursor = "none";
self.addChild(self.brush);
function startDrawing(evt) {
x = evt.stageX-0.001;
y = evt.stageY-0.001;
isDrawing = true;
draw(evt);
};
function stopDrawing() {
isDrawing = false;
};
function draw(evt) {
self.brush.x = self.mouseX;
self.brush.y = self.mouseY;
if (!isDrawing) {
self.update();
return;
}
overlay.graphics.clear();
// Eraser line
overlay.graphics
.ss(15, 1)
.s("rgba(30,30,30,1)")
.mt(x, y)
.lt(evt.stageX, evt.stageY);
overlay.updateCache("destination-out");
x = evt.stageX;
y = evt.stageY;
self.update();
$rootScope.$broadcast("LotteryChangeEvent");
};
}
Any ideas?

That's a tricky one, regardless of the language. The naive solution would simply be to track the length of the paths the user "draws" within the active area, and then reveal when they scratch long enough. That's obviously not very accurate, but is fairly simple and might be good enough.
The more accurate approach would be to get the pixel data of the cacheCanvas, then check the alpha value of each pixel to get an idea of how many pixels are transparent (have low alpha). You could optimize this significantly by only checking every N pixel (ex. every 5th pixel in every 5th row would run 25X faster).

Related

Is there anyway to replace some of the block data to add an image?

I am making a JS game which essentially involves the player catching falling types of food in their basket.
Currently I have the food abstracted as coloured falling blocks to get the base layer of the game running. But I am having difficulty in replacing some of the Block attributes with an image instead of showing coloured, square blocks.
Part of the code is from a tutorial to get me started, the source link is here:
- https://code.tutsplus.com/tutorials/develop-your-first-game-in-canvas-from-start-to-finish--pre-39198
Also, the same food-block problem can fix the basket image replacing the main block.
The image for the food + basket I should link as well
https://www.google.co.uk/search?safe=active&tbm=isch&sa=1&ei=109tXLmaDdeQ1fAPp-GZ4AU&q=carrot+graphic&oq=carrot+&gs_l=img.3.0.0i67l2j0l2j0i67j0l5.22035.23851..26200...0.0..0.66.406.7......1....1..gws-wiz-img.......35i39.ocV_1GqpqKY&safe=active#imgrc=HlN5WnY4Ah8SLM:
https://www.google.co.uk/search?safe=active&tbm=isch&q=basket+graphic&chips=q:basket+graphic,online_chips:picnic+basket,online_chips:icon+vector&usg=AI4_-kQ272sXU1YYZzeNyiyB65Ps8k7fgg&sa=X&ved=0ahUKEwi0pb-ZuMrgAhU_UhUIHQMRC5kQ4lYIKygB&biw=994&bih=919&dpr=1&safe=active#imgrc=9ijozvqskGZ1LM:
I am quite new to Stack Overflow and programming likewise, so I am open to feedback and recommendations that you may have.
Here is the script that I wrote to add the food image which should replace the block. This can already been seen in the HTML doc (I put temporarily).
<script>
var canvas = document.getElementById("js/FoodCatcher.js");
//Draw image for object
<img id="SourceImage" src="carrot.jpg">
function init() {
var image = document.getElementById('SourceImage');
canvas = document.getElementById('canvas');
context = canvas.document.getContext('2d');
drawImage(image);
}
function drawImage(image) {
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
}
window.addEventListener('load', init);
//Draw basket for basket object
<img id="BasketImage" src="basket.jpg">
function init(){
var image = document.getElementById('BasketImage');
canvas = document.getElementById('canvas');
context = canvas.document.getContext('2d');
drawImage(image);
}
function drawImage(image) {
canvas.width = image.width;
canvas.height = image.height;
context.drawImage(image, 0, 0);
}
</script>
Here is a portion of the JS code that I want to modify to add the image for the block.
FoodCatcher = new function() //This defines the current object along with the game colors + initiate/call things at right time
{
this.colors = [ //Public color appearance
'#f00', //Red color
'#0f0', //Green color
'#00f', //Blue Colors
];
var basketData = [ //Private
['width', 60], //pixel width of the basket 30
['height', 13], //Pixel height of the basket 10
['xSpeed', 10], //Horizontal movement speed 4
['color', '#f00'], //Color of basket
['oldColor', '#f00'] //Old color of basket, used to prevent having same color twice
];
var blockData = [
['width', 10], //pixel width of the basket 10
['height', 10], //Pixel height of the basket 10
['ySpeed', 5], //Horizontal movement speed 1
['color', undefined], //Color of block undefined
['strength', 30] //Points they gain/subtract 30
];
var Block = function(data) {
Movable.call(this, data);
this.initPosition();
this.initColor();
}
Block.prototype = new Movable();
Block.prototype.initPosition = function() {
// Only allow to set the position of this block once
if (this.x !== undefined || this.y !== undefined)
return;
// By picking a rounded number between 0 and the canvas.width substracted by the block its width, we have a position for this block which is still inside the block its viewport
this.x = Math.round(rand(0, canvas.width - this.width));
// By setting the vertical position of the block to 0 substracted by the block its height, the block will look like it slides into the canvas its viewport
this.y = 0 - this.height;
}
Block.prototype.initColor = function() {
if (this.color !== undefined)
return;
this.color = FoodCatcher.colors[Math.round(rand(0, (FoodCatcher.colors.length - 1)))];
}
Block.prototype.move = function() {
// Add the vertical speed to the block its current position to move it
this.y += this.ySpeed;
}
[Question edited on Thurs 21/03 07:50am]
All in all I would advise you to use a framework like Pixi.js to create your game, but if you are here to learn the basics, I'm here to help you out.
I guess you want to draw images instead of simple blocks. This consists of 3 steps:
1. add the data to blockData
We need to add the data of the image to the block. That can be a base64-data-url (f.e. you can use a website for this: 1) or a link to an image.
var blockData = [
['width', 100],
['height', 100],
['imageUrl', 'dataUrlOrImageUrl']
]
2. load the image
In the initialisation phase load the image by using an hidden image and setting the src-attribute.
Block.prototype.initImage = function () {
this.image = new window.Image();
// the onload callback will be run once the image is loaded
// use the onload callback if you want to avoid flicker
// this.onload = function(){alert('image loaded');}
this.image.src = this.imageUrl;
}
Of course you have to call the function somewhere somehow. I don't want to go deep inside the topic of callbacks, so I didn't use the onload-callback which will result in rendering a blank image till the image is fully loaded.
3. use the image
You can then draw your image like you did in your html test page.
Block.prototype.draw = function (context) {
// I'm guessing the position and the context here,
// but the first parameter should be the image, created in initImage
context.drawImage(this.image, this.x, this.y);
}

Images not displaying the first time in this object program in JS

I am making a battleship game with polar coordinates. After the user chooses two points, a battleship should be drawn in the middle. My Battleship constructor looks like this:
function Battleship(size, location, source){
this.size = size;
//initializing the image
this.image = new Image();
this.image.src = source;
this.getMiddlePoint = function(){
//get midpoint of ship
...
}
this.distanceBetween = function(t1, t2){
//dist between two points
}
this.display = function(){
var point = [this.radius];
point.push(this.getMiddlePoint());
point = polarToReal(point[0], point[1] * Math.PI / 12);
//now point has canvas coordinates of midpoint
var width = this.distanceBetween(this.info[0][0], this.info[this.info.length-1][0]);
var ratio = this.image.width / width;
ctx.drawImage(this.image, point[0] - width/2, point[1] - this.image.height / ratio / 2, width, this.image.height / ratio);
//draws the image
}
}
The display method of each ship gets called at a certain point (after the user has chosen the location). For some reason, the images do not show the first time I do this, but when I run this code at the very end:
for(var i = 0; i<playerMap.ships.length; i++){
playerMap.ships[i].display();
}
All ships are displayed correctly (not aligned well, but they are displayed). I think there is a problem with loading the images. I am not sure how to fix this. I tried using image.onload but I never got that to work. I also tried something like this:
var loadImage = function (url, ctx) {
var img = new Image();
img.src = url
img.onload = function () {
ctx.drawImage(img, 0, 0);
}
}
but the same problem kept happening. Please help me fix this problem. Here is the game in its current condition. If you place ships, nothing happens, but after you place 5 (or 10) ships, they suddenly all load.
EDIT:
I solved the problem by globally defining the images. This is still very bad practice, since I wanted this to be in the battleship object. This is my (temporary) solution:
var sub = [];
for(var i = 1; i<5; i++){
sub[i] = new Image();
sub[i].src = "/img/ships/battleship_"+i+".png";
}

HTML5 Canvas Drawing History

I'm curious to know how applications such as Adobe Photoshop implement their drawing history with the ability to go back or undo strokes on rasterized graphics without having to redraw each stroke from the beginning...
I'm wanting to implement a similar history function on an HTML5 drawing application I'm working on but duplicating the canvas after every stoke seems like it'd use too much memory to be a practical approach, especially on larger canvas'...
Any suggestions on how this might be implemented in a practical and efficient manner?
I may have a solution.....
var ctx = document.getElementById("canvasId").getContext("2d");
var DrawnSaves = new Array();
var Undo = new Array();
var FigureNumber = 0;
var deletingTimer;
function drawLine(startX, startY, destX, destY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(destX, destY);
ctx.stroke();
var Para = new Array();
Para["type"] = "line";
Para["fromX"] = startX;
Para["fromY"] = startY;
Para["toX"] = destX;
Para["toY"] = destY;
DrawnSaves.push(Para);
FigureNumber++;
}
function undo() {
ctx.beginPath();
ctx.clearRect(0, 0, 500, 500);
Undo[FigureNumber] = DrawnSaves[FigureNumber];
DrawnSaves[FigureNumber] = "deleted";
FigureNumber--;
drawEverything();
startTimeoutOfDeleting();
}
function undoTheUndo() {
FigureNumber++;
DrawnSaves[FigureNumber] = Undo[FigureNumber];
drawEverything();
clearTimeout(deletingTimer);
}
function drawEverything() {
for (i = 0; i < DrawnSaves.length; i++) {
if (DrawnSaves[i].type == "line") {
ctx.beginPath();
ctx.moveTo(DrawnSaves[i].fromX, DrawnSaves[i].fromY);
ctx.lineTo(DrawnSaves[i].toX, DrawnSaves[i].toY);
ctx.stroke();
}
}
}
function startTimeoutOfDeleting() {
setTimeout(function() {Undo[FigureNumber] = "deleted";}, 5000);
}
This is really simple, first I draw a line when the function is called and save all his parameters in an array. Then , in the undo function I just start a timer do delete the figure drawn i 2000 miliseconds, clears the whole canvas and makes it can't be redrawn. in the undoTheUndo function, it stops the timer to delete the figure and makes that the figure can be redrawn. In the drawEverything function, it draws everything in the array based on it's type ("line here"). That's it... :-)
Here is an example working : This, after 2sec UNDOs then after 1sec UNDOTHEUNDO

Changing canvas drawn image on mouseover

I'm really struggling with the changing of my canvas drawn image so I thought I would see if anyone could assist me on here or offer advice.
I've drawn a static flag in canvas, and I've also drawn a waving flag. I'm trying to get this flag to wave on mouseover.
I initially thought that I was going to have to create two separate files, one for the static and one for the waving aspect. Then save each of them as a jpg/gif image using window.location = canvas.toDataURL("image/");.
But I've just discovered that you can apparently do this all in the same file via jquery/hover. Which seems a lot simpler and a more efficient way of doing it.
Here is the code for the waving flag:
window.onload = function(){
var flag = document.getElementById('banglaFlag');
banglaStatic( flag, 320 );
var timer = banglaWave( flag, 30, 15, 200, 200 );
};
function banglaStatic( canvas, width ){
//Drawing the Bangladesh flag.
//Declaring variables that regard width and height of the canvas.
//Variables C to L are needed for the waving function.
var a = width / 1.9;
var b = 200;
var c = 7*a/13;
var l = a / 13;
canvas.width = b;
canvas.height = a;
var ctx = canvas.getContext('2d');
var radius = 45;
};
function banglaWave( canvas, wavelength, amplitude, period, shading ){
var fps = 30;
var ctx = canvas.getContext('2d');
var w = canvas.width, h = canvas.height;
var od = ctx.getImageData(0,0,w,h).data;
// var ct = 0, st=new Date;
return setInterval(function(){
var id = ctx.getImageData(0,0,w,h);
var d = id.data;
var now = (new Date)/period;
for (var y=0;y<h;++y){
var lastO=0,shade=0;
for (var x=0;x<w;++x){
var px = (y*w + x)*4;
var o = Math.sin(x/wavelength-now)*amplitude*x/w;
var opx = ((y+o<<0)*w + x)*4;
shade = (o-lastO)*shading;
d[px ] = od[opx ]+shade;
d[px+1] = od[opx+1]+shade;
d[px+2] = od[opx+2]+shade;
d[px+3] = od[opx+3];
lastO = o;
}
}
ctx.putImageData(id,0,0);
// if ((++ct)%100 == 0) console.log( 1000 * ct / (new Date - st));
},1000/fps);
}
Thanks in advance for any advice/assistance.
I am not sure where your problem is. I did not see any event handling code, so I assume that's your question:
Define a function to "handle the mouse event". For example, if you want to move the flag when the user moves the mouse over it, define something like:
function mouseMove(event) {
var mouseX,
mouseY;
event.preventDefault(); // stops browser to do what it normally does
// determine where mouse is
mouseX = event.pageX;
mouseY = event.pageY;
// do something useful, e.g. change the flag to waving when mouse is over flag
}
Then, register this function to be called when the mouse moves:
canvas.addEventListener("mousemove", mouseMove, false);
canvas is the canvas you paint the flag on, "mousemove" is the name of the event (many more exist, such as "mousedown", "mouseup", "mouseout" (leaving canvas), "mousewheel", etc.), mouseMove is the name of your function (the event handler, as it's called).
Events are a little different from browser to browser (and even browser version), so you might need to implement different event handler if you need it across browsers.
Hoping this helped...
canvas is like a sheet. there is no any object on which you can hover.
for doing what you wanted to do is just,bound an area on the flag,
follow the 'virtualnobi' answer and calculate if mouse co-ordinate falls on that region,
if true do what ever you want.
like
if (mouseX<100 && mouseX>0 && mouseY>0 && mouseY<100){
//animate the flag
}
use mouseX=event.clientX;
mouseY=event.clientY;
bounded area is x=(0,100) , y=(0,100) here.

How i can get hit test with image on canvas?

I create image in this way:
var orc = new Image();
orc.src = "./orc.png";
I use image in objects like this:
function Character(hp, image){
this.hp = hp;
this.image = image;
};
I call it in several times, like:
unit245 = new Character(100, orc);
And I draw it in this way, for example:
ctx.drawImage(unit245.image, 15, 55, 100, 100);
How I can get mouse click or move above my unit245 on canvas?
I need something like this http://easeljs.com/examples/dragAndDrop.html but without any frameworks (except jquery)
There is no built in way. I've written a few tutorials on making movable and selectable shapes on a Canvas to help people get started with this sort of thing though.
In short you need to remember what you have drawn and where, and then check each mouse click to see if you have clicked on something.
HitTesting can be done by checking what is present at the current location over the canvas, which can be called upon mouse click or move event over the canvas (which is the basis of hit testing). This can be done by knowing what has been placed where, like the bounds of an image can be saved, and when user clicks somewhere or moved the mouse over the canvas, you can check whether it is inside the image bounds or outside it. Array or List can be used for this.
Here is how this can be done
You cannot. The canvas has no semblance of what your unit245 or Character object is. You will have to actually manually check the coordinates and see if they fall within the bounds that you have for the character.
For example (assuming your Canvas is a var named canvas):
canvas.onclick = function(e) {
if (e.x >= unit245.x && e.x <= unit245.x + unit245.width && e.y >= unit245.y && e.y <= unit245.y + unit245.height) {
alert("You clicked unit245!");
}
}
In your case:
unit245.x = 15
unit245.y = 55
unit245.width = 100
unit245.height = 100
function Item(img, x, y){
this.image = img;
this.x = x;
this.y = y;
this.canv = document.createElement("canvas");
this.canv.width = this.image.width;
this.canv.height = this.image.height;
this.ctx = this.canv.getContext('2d');
this.ctx.drawImage(this.image, 0, 0, CELL_SIZE, CELL_SIZE);
this.hit = function (mx, my) {
var clr;
clr = this.ctx.getImageData(mx - this.x, my - this.y, 1, 1).data;
if (clr[3] > 250) {
//On object
this.image = gold_glow;
} else {
//Leave object
this.image = gold;
}
};
}

Categories