Canvas text rendering - javascript

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>

Related

How to store and restore canvas to use it again?

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.
*/

Split HTML Canvas into rows with JavaScript

I'm trying to create an animated glitch effect using JavaScript and HTML Canvas. I have managed to draw my image onto a canvas and want to write a separate function that later splits this image into rows so they can be animated individually.
I call my function to create the canvas...
var logo_text = "logo_text.png";
drawOnCanvas(logo_text, mc_logo_text, 0, 0, 245, 60);
And here's what the function looks like...
function drawOnCanvas(image, divID, posX, posY, wid, hei) {
//////////// CREATE CANVAS /////////////
var canvas = document.createElement("CANVAS"),
ctx = canvas.getContext("2d");
canvas.width = wid;
canvas.height = hei;
canvas.id = divID.id+"_canvas";
divID.appendChild(canvas);
//////////// DRAW IMAGE /////////////
var img = new Image();
img.src = image;
img.onload = function () {
ctx.drawImage(img, posX, posY, wid, hei);
} }
This function works fine and draws the image where I need it. I just can't figure out the best way to split this into rows.
Try this!
You just need to edit the settings in each object of _config array.
drawOnCanvas() will do same times equal the object's number in _config.
By the way, the scripts need to import below the DIVs , so document.getElementById could find them.
<body>
<div id="mc_logo_text1"></div>
<div id="mc_logo_text2"></div>
<div id="mc_logo_text3"></div>
<script>
// Simply edit all settings here
var _config = [
{
src: 'logo_text.png',
domDiv: 'mc_logo_text1',
position: {x: 0, y: 0},
size: {w: 245, h: 60}
},
{
src: 'logo_text.png',
domDiv: 'mc_logo_text2',
position: {x: 0, y: 0},
size: {w: 245, h: 60}
},
{
src: 'logo_text.png',
domDiv: 'mc_logo_text3',
position: {x: 0, y: 0},
size: {w: 245, h: 60}
}
]
/////// No Need to modify ////////
var drawOnCanvas = function(config) {
var canvas = document.createElement("CANVAS");
var ctx = canvas.getContext("2d");
//Define which DIV to include new Canvas
var domTaget = document.getElementById(config.domDiv);
// Create Canvas & give new ID name
function _canvasInit() {
canvas.width = config.size.w;
canvas.height = config.size.h;
canvas.id = domTaget.id + "_canvas";
domTaget.appendChild(canvas);
}
// draw images into canvas
function _drawImgs() {
var img = new Image();
img.src = config.src;
img.onload = function() {
ctx.drawImage(img, config.position.x, config.position.y, canvas.width, canvas.height);
}
}
// Do all functions
_canvasInit();
_drawImgs();
}
// Each object in _config array do drawOnCanvas function,
for (var i = 0; i < _config.length; i = i + 1) {
drawOnCanvas(_config[i]);
}
/////// No Need to modify ////////
</script>
</body>

canvas animation unintentionally drawn using multiple imgdata

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

Take Keyboard Input Into Ember Component

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

Using Canvas and Drawimage in functions with video

I am using the HTML5 canvas and DrawImage functions to display low rate video. tnt gave excellent advice to get this project off the ground: Trying to use Canvas and DrawImage to display low-rate video at 90 degrees
While tnt's solution worked fine when the camera and angle are known at load time by using onload functions, I need to be able to turn the video off and on between several cameras and change other parameters. To handle that, a number of individual functions are needed, but I haven't been able to first do a setInterval on a camera and then pass the constantly changing image to DrawImage. 'cam_1.jpg' is the video in the example below. The functions shown in body onload below will also have to be called by other routines during runtime. Any advice will be appreciated.
var cam = null;
var c = null;
var ctx = null;
var ra = 0;
function init() {
cam = new Image;
c = document.getElementsByTagName('canvas')[0];
ctx = c.getContext('2d');
}
function draw(cam) {
ctx.save();
ctx.clearRect( 0, 0, 240, 320 );
ctx.translate( 240, 0);
ctx.rotate(1.57);
ctx.drawImage(cam, 0, 0 );
ctx.restore();
}
function inter() {
setInterval(function(){cam.src = 'cam_1.jpg?uniq='+Math.random();},500);
}
</script></head><body onload = "init(), draw(cam), inter()" >
Thank you.
I suggest you use an object array; something like this:
var cams = []; // an array to hold you cams!
function addcam() {
this.image = new Image;
this.setting1 = 0;
this.settingn = 0;
}
cams[1] = addcam();
cams[1].image.src = "cam1.jpg";
cams[1].setting1 = 2;
setInterval(function(){cam.src = '+cams[1].image.src+'?uniq='+Math.random();},500);
#tnt, Are you suggesting the following? Thank you:
var cams = []; // an array to hold you cams!
function addcam() {
this.image = new Image;
this.setting1 = 0;
this.settingn = 0;
}
function draw(camnum){
cams[1] = addcam();
cams[1].image.src = "cam_1.jpg";
cams[1].setting1 = 2;
cam = new Image;
c = document.getElementsByTagName('canvas')[0];
ctx = c.getContext('2d');
}
function inter() {
setInterval(function(){'cam.src = ' +cams[1].image.src+ '?uniq='+Math.random();},500);
}
function draw(cam) {
ctx.save();
ctx.clearRect( 0, 0, 240, 320 );
ctx.translate( 240, 0);
ctx.rotate( 1.57);
ctx.drawImage(this, 0, 0 );
ctx.restore();
}
</script></head><body onload = "draw(cam), inter()" >

Categories

Resources