HTML Canvas & Javascript - Triggering Audio by Selection (From Multiple Places) - javascript

I have a selection menu in my HTML canvas that I would like to trigger corresponding audio files. I have tried implementing this by declaring the images inside the if (this.hovered) & (this.clicked) part of the makeSelection function within the selectionForMenu prototype, such that on each new selection the selected audio file is redefined, but this causes problems like slow loading and overlapping audio. It is also problematic as I am trying to get the speaker button at the bottom of the screen to play the audio corresponding to the current selection too, so if it is only defined within that function it is not accessible to the makeButton function.
You can see the selection menu and speaker button in the snippet below. Each new selection in the menu should play once an audio file that corresponds to it (which I have not been able to add to this demonstration). It can be replayed by re-clicking the selection or clicking the speaker button, but each click should only provoke one play of the audio and of course overlapping is undesired. Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var button = function(id, x, strokeColor) {
this.id = id;
this.x = x;
this.strokeColor = strokeColor;
this.hovered = false;
this.clicked = false;
}
button.prototype.makeInteractiveButton = function() {
if (this.hovered) {
if (this.clicked) {
this.fillColor = '#DFBCDE';
} else {
this.fillColor = '#CA92C8'
}
} else {
this.fillColor = '#BC77BA'
}
ctx.strokeStyle=this.strokeColor;
ctx.fillStyle=this.fillColor;
ctx.beginPath();
ctx.lineWidth='5';
ctx.arc(this.x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
}
button.prototype.hitTest = function(x, y) {
return (Math.pow(x-this.x, 2) + Math.pow(y-475, 2) < Math.pow(20, 2));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
}
selectionForMenu.prototype.makeSelection = function() {
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(0, this.y, 350, 30)
ctx.stroke();
ctx.font='10px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, 10, this.y+19);
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= 0) && (x <= (350)) && (y >= this.y) && (y <= (this.y+30)) && !((x >= 0) && (y > 450));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, 450, 750, 50);
ctx.stroke();
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeInteractiveButton();
}
catch(err) {}
}
var speaker = new Image(25, 25);
speaker.src='https://i.stack.imgur.com/lXg2I.png';
ctx.drawImage(speaker, 162.5, 462.5);
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = false;
if (Number.isInteger(this.shapes[i].id)) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var paint = new Paint(c);
var btn = new button('speaker', 175, '#FFFCF8');
var selection = [];
for (i=0; i<15; i++) {
selection.push(new selectionForMenu(i+1, i, i*30));
}
paint.addShape(btn);
for (i=0; i<15; i++) {
paint.addShape(selection[i])
}
paint.render();
function mouseDown(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setClicked(shape);
}
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setUnclicked(shape);
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
img {
z-index: 0;
position: absolute;
pointer-events: none;
}
#speaker {
top: 480px;
left: 592px;
}
#snail {
top: 475px;
left: 637.5px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'></style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
</head>
<body>
<canvas id="game" width = "350" height = "500"></canvas>
<script type='text/javascript' src='wordpractice copy.js'></script>
</body>
</html>

When you want responsiveness with audio, forget about MediaElements, and go with the Web Audio API.
MediaElements (<audio> and <video>) are slow, and http caching is an nightmare.
With the Web Audio API, you can first download all you media as arrayBuffers, decode their audio data to AudioBuffers, that you'll attach to your js objects.
From there, you'll be able to play new instances of these media in µs.
Beware, ES6 syntax below, for older browsers, here is an ES5 rewrite, also note that Internet Explorer < Edge does not support the Web Audio API, if you need to support these browsers, you'll have to make an fallback with audio elements.
(function myFirstDrumKit() {
const db_url = 'https://dl.dropboxusercontent.com/s/'; // all our medias are stored on dropbox
// we'll need to first load all the audios
function initAudios() {
const promises = drum.parts.map(part => {
return fetch(db_url + part.audio_src) // fetch the file
.then(resp => resp.arrayBuffer()) // as an arrayBuffer
.then(buf => drum.a_ctx.decodeAudioData(buf)) // then decode its audio data
.then(AudioBuf => {
part.buf = AudioBuf; // store the audioBuffer (won't change)
return Promise.resolve(part); // done
});
});
return Promise.all(promises); // when all are loaded
}
function initImages() {
// in this version we have only an static image,
// but we could have multiple per parts, with the same logic as for audios
var img = new Image();
img.src = db_url + drum.bg_src;
drum.bg = img;
return new Promise((res, rej) => {
img.onload = res;
img.onerror = rej;
});
}
let general_solo = false;
let part_solo = false;
const drum = {
a_ctx: new AudioContext(),
generate_sound: (part) => {
// called each time we need to play a source
const source = drum.a_ctx.createBufferSource();
source.buffer = part.buf;
source.connect(drum.gain);
// to keep only one playing at a time
// simply store this sourceNode, and stop the previous one
if(general_solo){
// stop all playing sources
drum.parts.forEach(p => (p.source && p.source.stop(0)));
}
else if (part_solo && part.source) {
// stop only the one of this part
part.source.stop(0);
}
// store the source
part.source = source;
source.start(0);
},
parts: [{
name: 'hihat',
x: 90,
y: 116,
w: 160,
h: 70,
audio_src: 'kbgd2jm7ezk3u3x/hihat.mp3'
},
{
name: 'snare',
x: 79,
y: 192,
w: 113,
h: 58,
audio_src: 'h2j6vm17r07jf03/snare.mp3'
},
{
name: 'kick',
x: 80,
y: 250,
w: 200,
h: 230,
audio_src: '1cdwpm3gca9mlo0/kick.mp3'
},
{
name: 'tom',
x: 290,
y: 210,
w: 110,
h: 80,
audio_src: 'h8pvqqol3ovyle8/tom.mp3'
}
],
bg_src: '0jkaeoxls18n3y5/_drumkit.jpg?dl=0',
};
drum.gain = drum.a_ctx.createGain();
drum.gain.gain.value = .5;
drum.gain.connect(drum.a_ctx.destination);
function initCanvas() {
const c = drum.canvas = document.createElement('canvas');
const ctx = drum.ctx = c.getContext('2d');
c.width = drum.bg.width;
c.height = drum.bg.height;
ctx.drawImage(drum.bg, 0, 0);
document.body.appendChild(c);
addEvents(c);
}
const isHover = (x, y) =>
(drum.parts.filter(p => (p.x < x && p.x + p.w > x && p.y < y && p.y + p.h > y))[0] || false);
function addEvents(canvas) {
let mouse_hovered = false;
canvas.addEventListener('mousemove', e => {
mouse_hovered = isHover(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop)
if (mouse_hovered) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
})
canvas.addEventListener('mousedown', e => {
e.preventDefault();
if (mouse_hovered) {
drum.generate_sound(mouse_hovered);
}
});
const checkboxes = document.querySelectorAll('input');
checkboxes[0].onchange = function() {
general_solo = this.checked;
general_solo && (checkboxes[1].checked = part_solo = true);
};
checkboxes[1].onchange = function() {
part_solo = this.checked;
!part_solo && (checkboxes[0].checked = general_solo = false);
};
}
Promise.all([initAudios(), initImages()])
.then(initCanvas);
})()
/*
Audio Samples are from https://sampleswap.org/filebrowser-new.php?d=DRUMS+%28FULL+KITS%29%2FSpasm+Kit%2F
Original image is from http://truimg.toysrus.co.uk/product/images/UK/0023095_CF0001.jpg?resize=500:500
*/
<label>general solo<input type="checkbox"></label><br>
<label>part solo<input type="checkbox"></label><br>

You could create an Audio Loader, that loads all the audios and keeps track of them:
function load(srcs){
var obj={};
srcs.forEach(src=>obj[src]=new Audio(src));
return obj;
}
Then you could do sth like this onload:
var audios=load(["audio1.mp3", "audio2.mp3"]);
And later:
(audios[src] || new Audio(src)).play();
This will just load the audio if it isnt already in the audios object.

Related

Undo with eraser stroke issue

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;
});

How to get visual representation of binary tree codding by javascript?

I created a binery tree using javascript but i don't know how to print it in html i tried a lot of methods but nothings work good, can someone help me in this ?
I tried canavas but i'm not perfect in this so it's doesn't work for me , i tried to append ul and li form javascript but it's give a lot of problem.
class Node {
constructor(data,left= null,right= null){
this.data = data;
this.left = left;
this.right = right;
}
}
// AB = Binery Tree
class AB {
constructor(){
this.root = null;
}
add(data,i){
const node = this.root;
console.log(data,i);
if(node === null){
this.root = new Node(data);
return;
}else{
const SEARCHTREE = function(node){
if(data <= node.data){
if(node.left === null){
node.left = new Node(data);
return;
} else if (node.left !== null){
return SEARCHTREE(node.left);
}
} else if (data > node.data) {
if(node.right === null){
node.right = new Node(data);
return;
}
else if (node.right !== null){
return SEARCHTREE(node.right);
}
} else {
return null;
}
};
return SEARCHTREE(node);
}
}
};
const ab = new AB();
for (var i = 0; i < 19; i++) {
ab.add(Math.floor(Math.random() * 10) + 1,i);
}
console.log(ab)
This the tree that i get :
I found the solution thank you.
this the js file :
class Node {
constructor(x,y,r, ctx, data){
this.data = data;
this.leftNode = null;
this.rightNode = null;
this.x = x;
this.y = y;
this.r = r;
this.ctx = ctx;
}
draw(){
this.ctx.beginPath();
this.ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI);
this.ctx.stroke();
this.ctx.closePath();
this.ctx.strokeText(this.data, this.x, this.y);
}
getData(){
return this.data;
}
getX(){
return this.x;
}
getY(){
return this.y;
}
getRadius(){
return this.r;
}
leftCoordinate(x,y,r){
return {cx: (x - (4*r)), cy: (y + (4*r))};
}
rightCoordinate(x,y,r){
return {cx: (x + (3*r)), cy: (y + (3*r))};
}
}
class Line {
// Takes
// x,y - starting x,y coordinate
// toX, toY - ending x,y coordinate
draw(x, y, toX, toY, r, ctx) {
var moveToX = x;
var moveToY = y + r;
var lineToX = toX;
var lineToY = toY - r;
ctx.beginPath();
ctx.moveTo(moveToX, moveToY);
ctx.lineTo(lineToX, lineToY);
ctx.stroke();
};
}
class BTree {
constructor(){
this.c = document.getElementById('my-canvas');
this.ctx = this.c.getContext('2d');
this.line = new Line();
this.root = null;
}
add(data){
// If root exists, then recursively find the place to add the new node
if(this.root) {
this.recursiveAddNode(this.root, null, null, data);
} else {
// If not, the add the element as a root
this.root = this.addAndDisplayNode(200, 20, 15, this.ctx,data);
return;
}
}
// Recurively traverse the tree and find the place to add the node
recursiveAddNode(node, prevNode, coordinateCallback, data) {
if(node === null) {
// This is either node.leftCoordinate or node.rightCoordinate
var xy = coordinateCallback;
var newNode = this.addAndDisplayNode(xy.cx, xy.cy, 15, prevNode.ctx, data);
this.line.draw(prevNode.getX(), prevNode.getY(), xy.cx, xy.cy, prevNode.getRadius(), prevNode.ctx);
return newNode;
}
else {
if(data < node.getData()) {
if(node.leftNode === null){
var xy = node.leftCoordinate(node.x,node.y,node.r);
node.leftNode = this.addAndDisplayNode(xy.cx, xy.cy, 15, node.ctx, data);
this.line.draw(node.getX(), node.getY(), xy.cx, xy.cy, node.getRadius(), node.ctx);
return;
}else if(node.leftNode !== null){
return
this.recursiveAddNode(node.leftNode,node,node.leftCoordinate(node.x,node.y,node.r), data);
}
}else if(data > node.getData()){
if(node.rightNode === null){
var xy = node.rightCoordinate(node.x,node.y,node.r);
node.rightNode = this.addAndDisplayNode(xy.cx, xy.cy, 15, node.ctx, data);
this.line.draw(node.getX(), node.getY(), xy.cx, xy.cy, node.getRadius(), node.ctx);
return;
}else if(node.rightNode !== null){
return
this.recursiveAddNode(node.rightNode,node,node.rightCoordinate(node.x,node.y,node.r), data);
}
}else {
return null;
}
}
}
// Adds the node to the tree and calls the draw function
addAndDisplayNode(x, y, r, ctx, data) {
var node = new Node(x, y, r, ctx, data);
node.draw();
return node;
}
}
var btree = new BTree();
for (var i = 0; i < 19; i++) {
btree.add(Math.floor(Math.random() * 10) + 1);
}
And this is html file :
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div class="tree center">
<canvas id='my-canvas' width="1000" height="1000">
Your browser doesnot support canvas
</canvas>
</div>
</body>
<script type="text/javascript" src="arbre.js"></script>
</html>
And this the css file :
.center {margin: auto; width: 50%;}

How to add object to an array properly in p5.js?

Ok first before you read the code, know this is a javascript library called P5.js
https://p5js.org
I noticed that my addMCS function was not adding an object to an array rather the screen goes blank and i get no response from the console.
var mcs = [];
function mouseCircle(x,y,s,color){
//constructor: mouseCircle(x,y,s,color)
this.x=x;
this.y=y;
this.s=s;
this.color = color;
this.mouseOver = false;
}
mouseCircle.prototype.mouseCollision = function(){
if(dist(this.x,this.y,mouseX,mouseY)<=this.s/2){
this.mouseOver=true;
}else{
this.mouseOver = false;
}
};
mouseCircle.prototype.addMCS = function(){
//THIS HERE IS THE ISSUE!!
mcs.push({this.x,this.y,this.s,this.color,this.mouseOver});
};
mouseCircle.prototype.Display = function(){
if(this.mouseOver) {
fill(this.color);
} else {
fill(255,255,255);
}
ellipse(this.x,this.y,this.s,this.s);
};
function setup() {
createCanvas(1000,650);
}
var mc1;
var mc2;
var mc3;
var mc2Color;
function draw() {
background(200,200,200);
mc1 = new mouseCircle(275,450,50,'green');//constructs 1st circle
mc1.mouseCollision();
mc1.Display();
mc1.addMCS();
console.log(mcs[0]);
/*
mc2Color = color(0,0,255);
mc2 = new mouseCircle(375,450,50,mc2Color);//constructs 2nd circle
mc2.mouseCollision();
mc2.Display();
mc3 = new mouseCircle(275,350,50,color(255,0,0));//constructs 3rd circle
mc3.mouseCollision();
mc3.Display();
mc4 = new mouseCircle(375,350,50,'yellow');//constructs 3rd circle
mc4.mouseCollision();
mc4.Display();
*/
//mouseCircle(575,150,50);
//mouseCircle(475,520,50);
//mouseCircle(375,150,50);
}
I cant see any sort of logic or syntax errors and I believe I have memories of using this method before (making buttons).
Why don't just use mcs.push(this); instead?
Working example:
var mcs = [];
function mouseCircle(x, y, s, color) {
//constructor: mouseCircle(x,y,s,color)
this.x = x;
this.y = y;
this.s = s;
this.color = color;
this.mouseOver = false;
}
mouseCircle.prototype.mouseCollision = function() {
if (dist(this.x, this.y, mouseX, mouseY) <= this.s / 2) {
this.mouseOver = true;
} else {
this.mouseOver = false;
}
};
mouseCircle.prototype.addMCS = function() {
mcs.push(this);
};
mouseCircle.prototype.Display = function() {
if (this.mouseOver) {
fill(this.color);
} else {
fill(255, 255, 255);
}
ellipse(this.x, this.y, this.s, this.s);
};
function setup() {
createCanvas(1000, 650);
}
var mc1;
var mc2;
var mc3;
var mc2Color;
function draw() {
background(200, 200, 200);
mc1 = new mouseCircle(275, 450, 50, 'green'); //constructs 1st circle
mc1.mouseCollision();
mc1.Display();
mc1.addMCS();
//console.log(mcs[0]);
/*
mc2Color = color(0,0,255);
mc2 = new mouseCircle(375,450,50,mc2Color);//constructs 2nd circle
mc2.mouseCollision();
mc2.Display();
mc3 = new mouseCircle(275,350,50,color(255,0,0));//constructs 3rd circle
mc3.mouseCollision();
mc3.Display();
mc4 = new mouseCircle(375,350,50,'yellow');//constructs 3rd circle
mc4.mouseCollision();
mc4.Display();
*/
//mouseCircle(575,150,50);
//mouseCircle(475,520,50);
//mouseCircle(375,150,50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.js"></script>

Why is this index html not displaying the game on my desk top?

I found this game online and am trying to get it to run from my desktop. Is there something special that a person has to do with the URL's or images to make the file recognize were everything is running and located at. I have all of the files and .png files in one folder and on the same level.
I would think that I should see the game on the screen. It is like a left to right horizontal scroll-er with enemy ships that come out of the right side of the screen and the main ship is on the right side of the screen. (Similar to that of the old style defender game)
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="app.css">
</head>
<body>
<div id="game-over-overlay"></div>
<div id="game-over">
<h1>GAME OVER</h1>
<button id="play-again">Play Again</button>
</div>
<div class="wrapper">
<div id="instructions">
<div>
move with <span class="key">arrows</span> or <span class="key">wasd</span>
</div>
<div>
shoot with <span class="key">space</span>
</div>
</div>
<div id="score"></div>
</div>
<script type="text/javascript" src="resources.js"></script>
<script type="text/javascript" src="input.js"></script>
<script type="text/javascript" src="sprite.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
sprite.js
(function() {
function Sprite(url, pos, size, speed, frames, dir, once) {
this.pos = pos;
this.size = size;
this.speed = typeof speed === 'number' ? speed : 0;
this.frames = frames;
this._index = 0;
this.url = url;
this.dir = dir || 'horizontal';
this.once = once;
};
Sprite.prototype = {
update: function(dt) {
this._index += this.speed*dt;
},
render: function(ctx) {
var frame;
if(this.speed > 0) {
var max = this.frames.length;
var idx = Math.floor(this._index);
frame = this.frames[idx % max];
if(this.once && idx >= max) {
this.done = true;
return;
}
}
else {
frame = 0;
}
var x = this.pos[0];
var y = this.pos[1];
if(this.dir == 'vertical') {
y += frame * this.size[1];
}
else {
x += frame * this.size[0];
}
ctx.drawImage(resources.get(this.url),
x, y,
this.size[0], this.size[1],
0, 0,
this.size[0], this.size[1]);
}
};
window.Sprite = Sprite;
resources.js
(function() {
var resourceCache = {};
var loading = [];
var readyCallbacks = [];
// Load an image url or an array of image urls
function load(urlOrArr) {
if(urlOrArr instanceof Array) {
urlOrArr.forEach(function(url) {
_load(url);
});
}
else {
_load(urlOrArr);
}
}
function _load(url) {
if(resourceCache[url]) {
return resourceCache[url];
}
else {
var img = new Image();
img.onload = function() {
resourceCache[url] = img;
if(isReady()) {
readyCallbacks.forEach(function(func) { func(); });
}
};
resourceCache[url] = false;
img.src = url;
}
}
function get(url) {
return resourceCache[url];
}
function isReady() {
var ready = true;
for(var k in resourceCache) {
if(resourceCache.hasOwnProperty(k) &&
!resourceCache[k]) {
ready = false;
}
}
return ready;
}
function onReady(func) {
readyCallbacks.push(func);
}
window.resources = {
load: load,
get: get,
onReady: onReady,
isReady: isReady
};
input.js
(function() {
var pressedKeys = {};
function setKey(event, status) {
var code = event.keyCode;
var key;
switch(code) {
case 32:
key = 'SPACE'; break;
case 37:
key = 'LEFT'; break;
case 38:
key = 'UP'; break;
case 39:
key = 'RIGHT'; break;
case 40:
key = 'DOWN'; break;
default:
key = String.fromCharCode(code);
}
pressedKeys[key] = status;
}
document.addEventListener('keydown', function(e) {
setKey(e, true);
});
document.addEventListener('keyup', function(e) {
setKey(e, false);
});
window.addEventListener('blur', function() {
pressedKeys = {};
});
window.input = {
isDown: function(key) {
return pressedKeys[key.toUpperCase()];
}
};
apps.js
// A cross-browser requestAnimationFrame
// See https://hacks.mozilla.org/2011/08/animating-with-javascript-from-setinterval-to-requestanimationframe/
var requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 512;
canvas.height = 480;
document.body.appendChild(canvas);
// The main game loop
var lastTime;
function main() {
var now = Date.now();
var dt = (now - lastTime) / 1000.0;
update(dt);
render();
lastTime = now;
requestAnimFrame(main);
};
function init() {
terrainPattern = ctx.createPattern(resources.get('terrain.png'), 'repeat');
document.getElementById('play-again').addEventListener('click', function() {
reset();
});
reset();
lastTime = Date.now();
main();
}
resources.load([
'sprites.png',
'terrain.png'
]);
resources.onReady(init);
// Game state
var player = {
pos: [0, 0],
sprite: new Sprite('sprites.png', [0, 0], [39, 39], 16, [0, 1])
};
var bullets = [];
var enemies = [];
var explosions = [];
var lastFire = Date.now();
var gameTime = 0;
var isGameOver;
var terrainPattern;
var score = 0;
var scoreEl = document.getElementById('score');
// Speed in pixels per second
var playerSpeed = 200;
var bulletSpeed = 500;
var enemySpeed = 100;
// Update game objects
function update(dt) {
gameTime += dt;
handleInput(dt);
updateEntities(dt);
// It gets harder over time by adding enemies using this
// equation: 1-.993^gameTime
if(Math.random() < 1 - Math.pow(.993, gameTime)) {
enemies.push({
pos: [canvas.width,
Math.random() * (canvas.height - 39)],
sprite: new Sprite('sprites.png', [0, 78], [80, 39],
6, [0, 1, 2, 3, 2, 1])
});
}
checkCollisions();
scoreEl.innerHTML = score;
};
function handleInput(dt) {
if(input.isDown('DOWN') || input.isDown('s')) {
player.pos[1] += playerSpeed * dt;
}
if(input.isDown('UP') || input.isDown('w')) {
player.pos[1] -= playerSpeed * dt;
}
if(input.isDown('LEFT') || input.isDown('a')) {
player.pos[0] -= playerSpeed * dt;
}
if(input.isDown('RIGHT') || input.isDown('d')) {
player.pos[0] += playerSpeed * dt;
}
if(input.isDown('SPACE') && !isGameOver && Date.now() - lastFire > 100) {
var x = player.pos[0] + player.sprite.size[0] / 2;
var y = player.pos[1] + player.sprite.size[1] / 2;
bullets.push({ pos: [x, y],
dir: 'forward',
sprite: new Sprite('sprites.png', [0, 39], [18, 8]) });
bullets.push({ pos: [x, y],
dir: 'up',
sprite: new Sprite('sprites.png', [0, 50], [9, 5]) });
bullets.push({ pos: [x, y],
dir: 'down',
sprite: new Sprite('sprites.png', [0, 60], [9, 5]) });
lastFire = Date.now();
}
}
function updateEntities(dt) {
// Update the player sprite animation
player.sprite.update(dt);
// Update all the bullets
for(var i=0; i<bullets.length; i++) {
var bullet = bullets[i];
switch(bullet.dir) {
case 'up': bullet.pos[1] -= bulletSpeed * dt; break;
case 'down': bullet.pos[1] += bulletSpeed * dt; break;
default:
bullet.pos[0] += bulletSpeed * dt;
}
// Remove the bullet if it goes offscreen
if(bullet.pos[1] < 0 || bullet.pos[1] > canvas.height ||
bullet.pos[0] > canvas.width) {
bullets.splice(i, 1);
i--;
}
}
// Update all the enemies
for(var i=0; i<enemies.length; i++) {
enemies[i].pos[0] -= enemySpeed * dt;
enemies[i].sprite.update(dt);
// Remove if offscreen
if(enemies[i].pos[0] + enemies[i].sprite.size[0] < 0) {
enemies.splice(i, 1);
i--;
}
}
// Update all the explosions
for(var i=0; i<explosions.length; i++) {
explosions[i].sprite.update(dt);
// Remove if animation is done
if(explosions[i].sprite.done) {
explosions.splice(i, 1);
i--;
}
}
}
// Collisions
function collides(x, y, r, b, x2, y2, r2, b2) {
return !(r <= x2 || x > r2 ||
b <= y2 || y > b2);
}
function boxCollides(pos, size, pos2, size2) {
return collides(pos[0], pos[1],
pos[0] + size[0], pos[1] + size[1],
pos2[0], pos2[1],
pos2[0] + size2[0], pos2[1] + size2[1]);
}
function checkCollisions() {
checkPlayerBounds();
// Run collision detection for all enemies and bullets
for(var i=0; i<enemies.length; i++) {
var pos = enemies[i].pos;
var size = enemies[i].sprite.size;
for(var j=0; j<bullets.length; j++) {
var pos2 = bullets[j].pos;
var size2 = bullets[j].sprite.size;
if(boxCollides(pos, size, pos2, size2)) {
// Remove the enemy
enemies.splice(i, 1);
i--;
// Add score
score += 100;
// Add an explosion
explosions.push({
pos: pos,
sprite: new Sprite('sprites.png',
[0, 117],
[39, 39],
16,
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
null,
true)
});
// Remove the bullet and stop this iteration
bullets.splice(j, 1);
break;
}
}
if(boxCollides(pos, size, player.pos, player.sprite.size)) {
gameOver();
}
}
}
function checkPlayerBounds() {
// Check bounds
if(player.pos[0] < 0) {
player.pos[0] = 0;
}
else if(player.pos[0] > canvas.width - player.sprite.size[0]) {
player.pos[0] = canvas.width - player.sprite.size[0];
}
if(player.pos[1] < 0) {
player.pos[1] = 0;
}
else if(player.pos[1] > canvas.height - player.sprite.size[1]) {
player.pos[1] = canvas.height - player.sprite.size[1];
}
}
// Draw everything
function render() {
ctx.fillStyle = terrainPattern;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Render the player if the game isn't over
if(!isGameOver) {
renderEntity(player);
}
renderEntities(bullets);
renderEntities(enemies);
renderEntities(explosions);
};
function renderEntities(list) {
for(var i=0; i<list.length; i++) {
renderEntity(list[i]);
}
}
function renderEntity(entity) {
ctx.save();
ctx.translate(entity.pos[0], entity.pos[1]);
entity.sprite.render(ctx);
ctx.restore();
}
// Game over
function gameOver() {
document.getElementById('game-over').style.display = 'block';
document.getElementById('game-over-overlay').style.display = 'block';
isGameOver = true;
}
// Reset game to original state
function reset() {
document.getElementById('game-over').style.display = 'none';
document.getElementById('game-over-overlay').style.display = 'none';
isGameOver = false;
gameTime = 0;
score = 0;
enemies = [];
bullets = [];
player.pos = [50, canvas.height / 2];
app.css
html, body {
margin: 0;
padding: 0;
background-color: #151515;
}
canvas {
display: block;
margin: auto;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.wrapper {
width: 512px;
margin: 0 auto;
margin-top: 2em;
}
#instructions {
float: left;
font-family: sans-serif;
color: #757575;
}
#score {
float: right;
color: white;
font-size: 2em;
}
.key {
color: #aaffdd;
}
#game-over, #game-over-overlay {
margin: auto;
width: 512px;
height: 480px;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
display: none;
}
#game-over-overlay {
background-color: black;
opacity: .5;
}
#game-over {
height: 200px;
text-align: center;
color: white;
}
#game-over h1 {
font-size: 3em;
font-family: sans-serif;
}
#game-over button {
font-size: 1.5em;
}
Here is the original game link
http://jlongster.com/Making-Sprite-based-Games-with-Canvas
If someone could put up a fiddle to see why it is not working it would be most appreciated.
Errors from the console
Uncaught SyntaxError: Unexpected end of input resources.js:61
Uncaught SyntaxError: Unexpected end of input input.js:43
Uncaught SyntaxError: Unexpected end of input sprite.js:54
Uncaught SyntaxError: Unexpected end of input app.js:302
Are you running this on a webserver? The way that you phrased it makes me think you you downloaded those files and just double-clicked index.html.
You need to download something like https://www.apachefriends.org/index.html and start the Apache service up, then put your files in the /xampp/htdocs/ folder... then goto
http://localhost/index.html
to get it to load.

Dealing with multiple animation state in one sprite sheet image using html5 canvas

I am recently creating a Game using html5 canvas .The player have multiple state it can walk jump kick and push and multiple other states my question is simple but after some deep research i couldn't find the best way to deal with those multiple states
this is my jsfiddle : http://jsfiddle.net/Z7a5h/5/
i managed to do one animation but i started my code in a messy way ,can anyone show me a way to deal with multiple state animation for one sprite image or just give a useful link to follow and understand the concept of it please .I appreciate your help
if (!this.IsWaiting) {
this.IsWaiting = true;
this.lastRenderTime = now;
this.Pos = 1 + (this.Pos + 1) % 3;
}
else {
if (now - this.lastRenderTime >= this.RenderRate) this.IsWaiting = false;
}
This is my animation class, which let's you set an animation and create the animation as object. I personally like to place the animations in an array such as playerAnimations[], and run the animations according to what the player does.
var toPix = function(n) {
return n*TILE; //tile is basically the same as sh or sw, but I used Tilesizes to draw things.
};
// Animations
var Sprite = function(image, sx, sy, sw, sh) {
this.img = image;
this.sx = sx;
this.sy = sy;
this.sw = sw;
this.sh = sh;
Sprite.prototype.draw = function(ctx, x, y) {
this.x = x;
this.y = y;
this.ctx = ctx;
this.ctx.drawImage(this.img, this.sx, this.sy, this.sw, this.sh, this.x, this.y, this.sw, this.sh);
};
};
var Animation = function(url, ctx, startingRow, rows, columns, sw, sh) {
this.ctx = ctx;
this.url = url;
this.startRow = toPix(startingRow - 1);
this.rows = rows;
this.columns = columns;
this.sprites = [];
animImg = new Image();
animImg.addEventListener('load', function() {});
animImg.src = this.url;
for(var i = 0; i < columns; i++) {
sprite = new Sprite(animImg, i*sw, this.startRow, sw, sh);
this.sprites.push(sprite);
}
this.spriteToDraw = 0;
this.drawSprite = 0;
this.drawSpriteTime = 10;
Animation.prototype.start = function() {
this.stopAnimation = false;
};
Animation.prototype.stop = function() {
this.stopAnimation = true;
};
Animation.prototype.draw = function(x, y) {
if(!this.stopAnimation) {
if(this.spriteToDraw < this.sprites.length) {
var sprite = this.sprites[this.spriteToDraw];
} else {
this.spriteToDraw = 0;
var sprite = this.sprites[this.spriteToDraw];
}
sprite.draw(this.ctx, x, y);
if(this.drawSprite > this.drawSpriteTime) {
this.spriteToDraw++;
this.drawSprite = 0;
} else {
this.drawSprite += 1;
}
}
};
};
//var animation = new Animation('theSprite.png', 5, 5, 45, 45);
//playerAnimations.push(animation);
And then this would be a sample player.draw() function.action.
What it does is: it checks which state the player is in, stops all other animations and runs the correct animation for that state.
player.prototype.draw = function() {
//player.draw function
if(this.playerRight) {
if (this.playerAnimation = playerAnimations[0]) {
this.playerAnimation.stop();
}
if (this.playerAnimation = playerAnimations[2]) {
this.playerAnimation.stop();
}
this.playerAnimation = playerAnimations[1];
this.playerAnimation.start();
this.playerAnimation.draw(this.x, this.y);
} else if(!this.playerRight && !this.playerLeft) {
if (this.playerAnimation = playerAnimations[1]) {
this.playerAnimation.stop();
}
if (this.playerAnimation = playerAnimations[2]) {
this.playerAnimation.stop();
}
this.playerAnimation = playerAnimations[0];
this.playerAnimation.start();
this.playerAnimation.draw(this.x, this.y);
} else {
if(this.playerLeft) {
if (this.playerAnimation = playerAnimations[0]) {
this.playerAnimation.stop();
}
if (this.playerAnimation = playerAnimations[1]) {
this.playerAnimation.stop();
}
this.playerAnimation = playerAnimations[2];
this.playerAnimation.start();
this.playerAnimation.draw(this.x, this.y);
}
};
I hope this is able to help you. This is my way of doing these kind of animations and it works for me, good luck!

Categories