I've created an ember component with a rectangular block inside a green canvas.
What I'm having trouble with is adding a keyboard-input command for A S D W to move the rectangle around the canvas. It's easy enough to do in regular javascript or jquery but inside the component model I'm a bit lost. Any help regarding the function would be very useful.
Linked here is an ember javascript bin: http://emberjs.jsbin.com/miyatoti/1/edit
Here is my present code of the component.
App.BoxGameComponent = Em.Component.extend({
tagName:'canvas',
width: 325,
height: 280,
refresh:30,
attributeBindings:['width', 'height'],
stars:null,
on:false,
build: function(){
var canvas = this.$()[0],
ctx = canvas.getContext('2d'),
shippos = [150, 120],
height = this.get('height'),
width = this.get('width');
this.set('shippos', shippos);
this.set('ctx', ctx);
this.set('on', true);
}.on('didInsertElement'),
kill: function(){
this.set('on', false);
}.on('willDestroyElement'),
clear: function () {
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width');
ctx.fillStyle = 'green';
ctx.clearRect(0, 0, width, height);
ctx.beginPath();
ctx.rect(0, 0, width, height);
ctx.closePath();
ctx.fill();
},
box: function () {
var that = this;
var ctx = this.get('ctx'),
height = this.get('height'),
width = this.get('width'),
shippos = this.get('shippos');
var posx = shippos[0],
posy = shippos[1];
ctx.rect(posx,posy,50,50);
ctx.stroke();
},
game: function(){
if(this.get('on')){
this.loop();
}
}.observes('on'),
loop: function () {
var refreshRate = this.get('refresh');
this.clear();
this.box();
if(this.get('on')){
Em.run.later(this, this.loop, refreshRate);
}
}
});
If anyone can help I've been slamming my brain at this for hours.
Hooking up keyup a canvas element is a bit trickier since the canvas doesn't get focus. So you just hook up to the window (and then destroy it later).
$(window).on('keyup', {self:this}, this.handleKeyUp );
http://emberjs.jsbin.com/miyatoti/2/edit
Related
I can really use some help, my goal is to add few lines to the canvas in different places, it means to use the function drawText few times, for some reason if I don't use drawText inside the onload of drawImage the text does not rendering I am really stuck and can use some help, my main target it to make website that can edit pictures and make memes (e.g.: https://imgflip.com/memegenerator)and adding text line is what i am getting stuck on because I don't understand how to render new text line while save the old one, every time i start new line its like the image rendering all over again and start from the beginning.
// Function to load image on the canvas
function drawImg(url) {
gCurrLineIdx = getCurrLineIdx();
let currLine = gMeme.lines[gCurrLineIdx];
const img = new Image();
img.src = url;
img.onload = () => {
gCtx.drawImage(img, 0, 0, gElCanvas.width, gElCanvas.height);
//If you dont call drawText the text does not render to the canvas
drawText(currLine.txt, currLine.size, currLine.fontColor, currLine.strokeColor, currLine.align, currLine.font, gElCanvas.width / 2, currLine.y);
};
}
// Function to add text on the canvas
function drawText(text = '', fontSize = 20, fontColor = 'white', strokeColor = 'black', align = 'center', font = "ariel", x = gElCanvas.width / 2, y = 20) {
gCtx.strokeStyle = strokeColor;
gCtx.fillStyle = fontColor;
gCtx.font = `${fontSize}px ${font}`;
gCtx.textAlign = align;
gCtx.fillText(text, x, y);
gCtx.strokeText(text, x, y);
}
//Function to add new text line on the canvas.
function onAddTextLine() {
let textInput = document.getElementById('text-input');
textInput.value = '';
addTextLine();
gCurrLineIdx = getCurrLineIdx();
var currLine = gMeme.lines[gCurrLineIdx];
drawText(currLine.txt, currLine.size, currLine.fontColor, currLine.strokeColor, currLine.align, currLine.font, gElCanvas.width / 2, currLine.y);
}
The question I see here is:
how to render new text line while save the old one
Looks like in your code you are drawing things independently, below is a different approach the class Meme has a few functions to add text and image but every time anything change the entire canvas is cleared (clearRect) and every element is re-drawn, the magic is in the this.draw(), that is the call to the common function that does all the drawing.
To keep my sample small I hard-coded many of the things you had as parameters, it should be easy for you to reintroduce them if you need
class Meme {
constructor(ctx, width, height) {
this.ctx = ctx;
this.ctx.font = '80px Arial';
this.ctx.fillStyle = "blue"
this.ctx.textAlign = "center"
this.width = width;
this.height = height;
}
addHeadText(text) {
this.htext = text;
this.draw();
}
addFootText(text) {
this.ftext = text;
this.draw();
}
addImage(image_src) {
this.image = new Image();
this.image.src = image_src;
this.image.onload = () => {
this.draw()
};
}
draw() {
this.ctx.beginPath();
this.ctx.clearRect(0, 0, this.width, this.height)
if (this.image) {
this.ctx.drawImage(this.image, 0, 0, this.width, this.height);
}
if (this.htext) {
this.ctx.textBaseline = "top";
this.ctx.fillText(this.htext, this.width / 2, 0);
}
if (this.ftext) {
this.ctx.textBaseline = "bottom";
this.ctx.fillText(this.ftext, this.width / 2, this.height);
}
this.ctx.closePath();
}
}
const canvas = document.getElementById('c');
const ctx = canvas.getContext('2d');
meme = new Meme(ctx, canvas.width, canvas.height)
meme.addHeadText("Hello");
meme.addFootText("World");
meme.addImage("http://i.stack.imgur.com/UFBxY.png");
document.getElementById('htext').onkeyup = (event) => {
meme.addHeadText(event.srcElement.value);
};
document.getElementById('ftext').onkeyup = (event) => {
meme.addFootText(event.srcElement.value);
};
Head Text:<input type="text" id="htext" name="ftext" style="width: 140px;" value="Hello"><br>
Foot Text:<input type="text" id="ftext" name="ftext" style="width: 140px;" value="World"><br>
<canvas id="c" width=280 height=380 style="border:2px solid red;"></canvas>
I am using PDF.js to show PDF in browser. PDF.js uses canvas to render PDF. I have js scripts that draws the lines on the canvas when user double clicks on the canvas. It also adds X check mark to remove the already drawn line.
based on my research i cannot simply just remove the line from the canvas because underneath pixels are gone when you draw something on it. To get it working i have to store lines and then clear canvas and re-load canvas and re-draw lines
Issue
I am not able to store canvas and restore canvas. When i click on X i was able to get lines re-drawn but canvas does not get restored. Canvas remains blank
Run the demo in full page
$(function () {
var $canvas = $("#myCanvas");
var canvasEl = $canvas.get(0);
var ctx = canvasEl.getContext("2d");
var lines = [];
var backupCanvas = document.createElement("canvas");
var loadingTask = pdfjsLib.getDocument('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf');
loadingTask.promise.then(function (doc) {
console.log("This file has " + doc._pdfInfo.numPages + " pages");
doc.getPage(1).then(page => {
var scale = 1;
var viewPort = page.getViewport(scale);
canvasEl.width = viewPort.width;
canvasEl.height = viewPort.height;
canvasEl.style.width = "100%";
canvasEl.style.height = "100%";
var wrapper = document.getElementById("wrapperDiv");
wrapper.style.width = Math.floor(viewPort.width / scale) + 'px';
wrapper.style.height = Math.floor(viewPort.height / scale) + 'px';
page.render({
canvasContext: ctx,
viewport: viewPort
});
storeCanvas();
});
});
function storeCanvas() {
backupCanvas.width = canvasEl.width;
backupCanvas.height = canvasEl.height;
backupCanvas.ctx = backupCanvas.getContext("2d");
backupCanvas.ctx.drawImage(canvasEl, 0, 0);
}
function restoreCanvas() {
ctx.drawImage(backupCanvas, 0, 0);
}
$canvas.dblclick(function (e) {
var mousePos = getMousePos(canvasEl, e);
var line = { startX: 0, startY: mousePos.Y, endX: canvasEl.width, endY: mousePos.Y, pageY: e.pageY };
lines.push(line);
drawLine(line, lines.length - 1);
});
function drawLine(line, index) {
// draw line
ctx.beginPath();
ctx.strokeStyle = '#df4b26';
ctx.moveTo(line.startX, line.startY);
ctx.lineTo(line.endX, line.endY);
ctx.closePath();
ctx.stroke();
// add remove mark
var top = line.pageY;
var left = canvasEl.width + 20;
var $a = $("<a href='#' class='w-remove-line'>")
.data("line-index", index)
.attr("style", "line-height:0")
.css({ top: top, left: left, position: 'absolute' })
.html("x")
.click(function () {
var index = $(this).data("line-index");
$(".w-remove-line").remove();
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// restore canvas
restoreCanvas();
lines.splice(index, 1);
for (var i = 0; i < lines.length; i++) {
drawLine(lines[i], i);
}
});
$("body").append($a);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
X: Math.floor(evt.clientX - rect.left),
Y: Math.floor(evt.clientY - rect.top),
};
}
});
canvas {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<b> Double Click on PDF to draw line and then click on X to remove lines</b>
<div id="wrapperDiv">
<canvas id="myCanvas"></canvas>
</div>
The PDF.js render() function is async so you need to store the canvas after the render has finished. Your code is firing storeCanvas() too early and storing a blank canvas. Easy fix, render() returns a promise so ...
page.render({
canvasContext: ctx,
viewport: viewPort
}).then( () => {
storeCanvas();
});
https://jsfiddle.net/fyLant01/1/
Reference: from https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L998
/**
* Begins the process of rendering a page to the desired context.
* #param {RenderParameters} params Page render parameters.
* #return {RenderTask} An object that contains the promise, which
* is resolved when the page finishes rendering.
*/
I have a function that draw a CANVAS line and make this get the same coordinates of a <div> by using offsetLeft and move its searching the same position of the <div>. It is working good.
drawCanvas() {
const c = document.getElementById("canvas");
const lineH = c.getContext("2d");
c.width = window.innerWidth;
c.height = window.innerHeight;
const positionCanvas = () => {
const divPosition = document.querySelector('.myDiv').offsetLeft
lineV.fillStyle = "grey";
lineV.fillRect(divPosition , 0, 2, window.innerHeight);
lineV.fill();
}
positionCanvas()
window.onresize = () => {
lineV.height = window.innerHeight;
positionCanvas()
}
The problem is I don't know how avoid the default CANVAS behavior that duplicates many times the line when I resize the windows. How do I solve it? Thank you
The answer is:
const positionCanvas = () => {
lineV.clearRect(0, 0, c.width, c.height); //<-- new line added in the code
//... rest of the code ommited
I have a div that contains an img tag and a canvas. When I drag an image into the div, the img.src changes, the new picture appears, and in the canvas, a pointillization animation using the data from the img tag is drawn.
When I drag and drop imgA into the div, imgA appears in the img tag, and the canvas does the animation using imgA, which is what I want.
The problem is here:
From this point, if I drag and drop imgB into the div, imgB appears in the img tag, BUT, the canvas animation uses imgA + imgB data.
I think what I did is this:
I drag and drop an image into the div.
The img.src changes
Once theimg is loaded, i resize and clear the canvas
I draw the new img on the canvas
Function pointillize takes the new canvas data and uses it in the animation which it draws onto the canvas
I thought it had something to do with clearRect() and I tried implementing some of the suggestions but I don't think it's that. I added a click event listener to the document and it does indeed clear the canvas.
Could it have something to do with setInterval? I'm just thinking now that maybe, when the new image is loaded it triggers setInterval to run concurrently to the previous setInterval()? Maybe the solution is to kill all previous setInterval functions upon loading a new img?
EDIT: It works perfectly now. I declared draw a global variable, and inside function pointillize() I clearInterval(draw) before I set draw = setInterval(). This wipes the setInterval function clean before I start a new one.
html:
<div id='container'>
<img id='output' src='file.jpg'/>
</div>
javascript
var container, output;
var c, ctx, draw; // draw is declared here as a global variable
window.onload = function(e) {
c = document.createElement('canvas');
ctx = c.getContext('2d');
document.getElementById('container').appendChild(c);
output = document.getElementById('output');
output.width = '400';
container = document.getElementById('container');
container.style.border = '2px dashed rgb(200, 200, 200)';
container.style.padding = '6px';
container.addEventListener('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(50, 50, 50)';
}, false);
container.addEventListener('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(50, 50, 50)';
}, false);
container.addEventListener('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(200, 200, 200)';
}, false);
container.addEventListener('drop', function(e) {
e.stopPropagation();
e.preventDefault();
this.style.border = '2px dashed rgb(200, 200, 200)';
var reader = new FileReader();
reader.addEventListener('load', function(e) {
output.src = e.target.result;
// once the new src img is loaded
output.onload = function() {
c.width = output.width;
c.height = output.height;
ctx.clearRect(0, 0, c.width, c.height);
ctx.drawImage(output, 0, 0, output.width, output.height);
pointillize(ctx);
}
}, false);
reader.readAsDataURL(e.dataTransfer.files[0]); // this line!
}, false);
}
function pointillize(context) {
clearInterval(draw); // before a new animation, clear setInterval
// get new image data
var imgData = context.getImageData(0, 0, c.width, c.height);
// clear canvas
ctx.beginPath();
ctx.rect(0, 0, output.width, output.height);
ctx.fillStyle = 'rgba(255, 255, 255, 1)';
ctx.fill();
// store the setInterval in a variable so it can be cleared
var draw = setInterval(function() {
for (var i = 0; i < 100; i++) {
// pick random integers x and y
var y = Math.floor(Math.random()*output.height);
var x = Math.floor(Math.random()*output.width);
// get its position in the array
var loc = (y*output.width + x)*4;
// alpha in rgba goes from 0 to 1
var r = imgData.data[loc];
var g = imgData.data[loc+1];
var b = imgData.data[loc+2];
var a = 0.5;
ctx.beginPath();
ctx.arc(x, y, 2, 0, 2*Math.PI);
ctx.fillStyle = 'rgb(' + r + ',' + g + ', ' + b + ',' + a + ')';
ctx.fill()
}
}, 50); // closing setInterval()
}
document.addEventListener('click', clearCanvas, false);
function clearCanvas() {
//interesting, so clearRect is working, for one frame...
ctx.clearRect(0, 0, c.width, c.height);
}
So, be this code, drawing a line in canvas:
var x = document.getElementById("canvas").onclick = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.moveTo(0, 0);
context.lineTo(300, 150);
context.stroke();
}
I want, for example to alert if the line was drawn. How can I do that? I mean something like if(x===true) alert("aa"); I know it doesn't works in js.
document.getElementById("canvas").onclick = function() {
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.moveTo(0, 0);
context.lineTo(300, 150);
context.stroke();
alert("aa"); /* here */
}
There is no change() event for <canvas>, so you cannot attach an event handler to the canvas element as you would to a div or to an input element.
However, this question has some other ideas:
https://stackoverflow.com/a/4649358