Unable to clear the canvas - javascript

I don't know why, but the clearRect() does not work.
This is a function that is executed every time I click on a panel or every time I change the thickness' value.
//When click on a menu section
$(".menuSection").click(function() {
//Show hide the selected item
$(this).next().toggle();
//hide all the other panels
$(this).next().siblings(".panel").hide();
//To update the values in the colors panel when selected
if ( $(this).prop("id") == "colors" ) {
changeColorBySliders();
}
if ( $(this).prop("id") == "pen" ) {
updatePreview();
}
});
//when thickness slider change:
$("#penPanel input[type=range]").on("input", updatePreview);
The fact is that it draws the line, but if I click it again it's doesn't reset.
var $preview = $("#preview");
var contextPreview = $preview[0].getContext("2d");
//Function to update the pen's preview
function updatePreview() {
console.log($("#thickness").val());
var rgba = $("#mainCanvas").css("background-color");
$("#preview").css("background-color", rgba);
//Resetting the preview
contextPreview.clearRect(0, 0, $preview.width, $preview.height);
//Drawing an example path
contextPreview.beginPath();
contextPreview.moveTo(50, 30);
contextPreview.lineTo(100, 110);
contextPreview.quadraticCurveTo(115, 120, 125, 80);
contextPreview.bezierCurveTo(145, -40, 150, 120, 200, 95);
contextPreview.lineTo(250, 65);
contextPreview.lineWidth = $("#thickness").val();
contextPreview.strokeStyle = color;
contextPreview.stroke();
contextPreview.closePath();
}
Any help?

Solution
I solved the problem using this kind of declaration:
var preview = document.getElementById("preview");
var contextPreview = preview.getContext("2d");
contextPreview.clearRect(0, 0, preview.width, preview.height);
Insted of the jquery one:
var $preview = $("#preview");
var contextPreview = $preview[0].getContext("2d");
Why?
it's because $preview is a jQuery object, $preview.width is therefore a function, so when you called clearRect(), you were actually calling contextPreview.clearRect(0,0, function, function) so your rect dimensions were undefined or 0 so it wasn't clearing a single pixel.
So you can still use jQuery but be sure you call contextPreview.clearRect(0,0, $preview[0].width, $preview[0].height)
var preview = document.getElementById("preview");
var contextPreview = preview.getContext("2d");
contextPreview.clearRect(0,0, $preview[0].width, $preview[0].height)
Special Thanks to Kaiido.

Related

show each div content individually in each page

I am exporting the data present inside the div to PDF when user click on the export button. I want to show each div content to show in individual pages inside the PDF.
The above scenario is working in the demo https://plnkr.co/edit/KvkVlYmmmJiZ71sghb1l?p=preview
The same when applied to below code, it is not working.
Demo here : https://plnkr.co/edit/P9nUSRY5TytkonM6dUHl?p=preview
js code:
$scope.export = function() {
var pdf = new jsPDF('landscape');
var source = $('#append-source');
$('.myDivClass').each(function(){
var html = "<div>"+$(this) + "</div><!--ADD_PAGE-->";//the code is broken with this line
// var html = $(this);
source.append(html);
});
console.log(source);
pdf.addHTML(
source, 0, 0, {
pagesplit: true
},
function(dispose){
pdf.save('test3.pdf');
}
);
}
It is not recommended to use jquery like this inside an angular app. To see why look here: Can we use both jQuery and Angular in our Web Application?
However what you want to do is possible if you put the following into your controller:
$scope.export = function() {
var pdf = new jsPDF('landscape');
var source = "";
var width1 = pdf.internal.pageSize.width;
$('.myDivClass').each(function(){
var textForPdfPage = $(this).children().eq(1).children()[0].textContent;
var html = "<div>"+ textForPdfPage + " </div><!--ADD_PAGE-->";
source+=html;
});
margins = {
top: 80,
bottom: 60,
left: 10,
width: '100%'
};
pdf.fromHTML(
source, // HTML string or DOM elem ref.
margins.left, // x coord
margins.top, { // y coord
'width': width1 // max width of content on PDF
},
function (dispose) {
pdf.save('test.pdf');
},
margins
);
}
Your main problem is that when you where trying to create your html string you only used $(this). $(this) gives you a jquery object. The string you want to put on the page is inside this object and is accessed using the jquery .children() method.
Here is a way of doing what you asked using addHTML() instead of fromHTML():
$scope.export = function() {
var pdf = new jsPDF('landscape');
var pdfName = 'test.pdf';
var options = {};
var $divs = $('.myDivClass') //jQuery object of all the myDivClass divs
var numRecursionsNeeded = $divs.length -1; //the number of times we need to call addHtml (once per div)
var currentRecursion=0;
//Found a trick for using addHtml more than once per pdf. Call addHtml in the callback function of addHtml recursively.
function recursiveAddHtmlAndSave(currentRecursion, totalRecursions){
//Once we have done all the divs save the pdf
if(currentRecursion==totalRecursions){
pdf.save(pdfName);
}else{
currentRecursion++;
pdf.addPage();
//$('.myDivClass')[currentRecursion] selects one of the divs out of the jquery collection as a html element
//addHtml requires an html element. Not a string like fromHtml.
pdf.addHTML($('.myDivClass')[currentRecursion], 15, 20, options, function(){
console.log(currentRecursion);
recursiveAddHtmlAndSave(currentRecursion, totalRecursions)
});
}
}
pdf.addHTML($('.myDivClass')[currentRecursion], 15, 20, options, function(){
recursiveAddHtmlAndSave(currentRecursion, numRecursionsNeeded);
});
}
I left the other answer so people can see both ways of doing it.

HTML Canvas - passing a background image to Canvas while it's also passing base64 data

I'm currently trying to do some Javascript work in Laserfiche forms which requires me to save the base64 data for an image in a separate text area, and feed that data back into the image to allow the canvas to be turned into an image in which I can save into the system.
The issue is I'm trying to have a background image in which the user can draw on (in this case, a vehicle that they can draw a circle on to indicate where the damage is). I am using sketch.js to allow the drawing part of the task.
From what I've read is that the background CSS cannot be saved into the canvas. That's fine but how do I pass the background image when I'm already grabbing the base64 data and passing that back into my canvas?
The saveimage class belongs to the text area and the imagefinish belong to the checkbox that they mark when the image is ready
html
<div class="demo" id="colors_demo">
<div class="tools">
Marker
Eraser
Download
</div>
<canvas id="colors_sketch" width="750" height="500" style="border:2px solid #000000 ; background: url(http://localhost/forms/img/vanImage.jpg"></canvas>
</div>
<input name="Field11" id="Field11-0" type="checkbox" value="isLocked" vo="e" data-parsley-class-handler="#Field11" data-parsley-errors-container="#Field11" data-parsley-multiple="Field11">
<textarea id="Field13" name="Field13" aria-labelledby="Field13" class="cf-medium" rows="3" vo="e" data-parsley-id="28"></textarea>
Javascript
//submitted form
if ($('[name=IsLocked]').val() == 'True') {
var myCanvas = document.getElementById('colors_sketch');
var ctx = myCanvas.getContext('2d');
var img = new Image;
img.onload = function() {
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
}
img.src = $('.saveimage .cf-field').text();
}
else {
//fill form
//$.getScript('http://localhost/Forms/js/sketch.js', function() {
$.getScript('https://rawgit.com/intridea/sketch.js/gh-pages/lib/sketch.js', function() {
//script is loaded and executed put your dependent JS here
$.each(['#f00', '#ff0', '#0f0', '#0ff', '#00f', '#f0f', '#000', '#fff'], function() {
$('#colors_demo .tools').append("<a href='#colors_sketch' data-color='" + this + "' style='width: 10px; background: " + this + ";'></a> ");
});
$.each([3, 5, 10, 15], function() {
$('#colors_demo .tools').append("<a href='#colors_sketch' data-size='" + this + "' style='background: #ccc'>" + this + "</a> ");
});
//$('#colors_sketch').sketch();
$('#colors_sketch').sketch({defaultColor: "#000"});
});
$('.imagefinish input').change(function(){
if(this.checked) {
var myCanvas = document.getElementById('colors_sketch');
$('.saveimage textarea').val(myCanvas.toDataURL());
}
});
}
I was able to add an image by adding a variable for my image path
var image = 'http://localhost/forms/img/vanImage.jpg'
and I also added the two lines to my onload for the style of "myCanvas". I have a feeling that this solution will only work because of how Laserfiches forms software works but the marked answer is also correct.
img.onload = function() {
ctx.drawImage(this, 0, 0, myCanvas.width, myCanvas.height);
myCanvas.style.backgroundRepeat = "no-repeat";
myCanvas.style.backgroundImage = 'url('+image+')'
}
img.src = $('.saveimage .cf-field').text();
}
Load the background image on page load, or an appropriate time and when the client is ready, draw the background onto the canvas behind the user content using ctx.globalCompositeOperation = "destination-over";
Create an image to hold the background.
const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";
You need to ensure that the image has loaded when the client clicks ready, incase it has not loaded you can set a callback that will call back the ready function if needed.
var backgroundLoadedCallback = null;
backgroundImage.onload = function(){
if( typeof backgroundLoadedCallback === "function"){
backgroundLoadedCallback();
}
}
Then create the canvas -> textarea function
function canvasToTextarea(){
const myCanvas = document.getElementById('colors_sketch');
const ctx = myCanvas.getContext("2d");
ctx.globalCompositeOperation = "destination-over"; // make sure the background go under the drawn pixels
// draw and fit background to the canvas
ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
// then convert to URL for textarea
$('.saveimage textarea').val(myCanvas.toDataURL());
}
In the checked function
$('.imagefinish input').change(function(){
if(this.checked) {
if(backgroundImage.complete) { // image has loaded so all good
canvasToTextarea(); // put canvas data URL to textarea
} else { // background has not yet loaded (or could be an error you will have to deal with that as well
// set the callback to the canvasToTextarea function
backgroundLoadedCallback = canvasToTextarea;
// when the background has loaded the canvas and background
// will be moved to the textarea as a data URL
}
}
});
Or modify sketch.js
Below is the draw function from sketch.js (and it`s very old school)
Sketch.prototype.redraw = function() {
var sketch;
this.el.width = this.canvas.width();
this.context = this.el.getContext('2d');
sketch = this;
$.each(this.actions, function() {
if (this.tool) {
return $.sketch.tools[this.tool].draw.call(sketch, this);
}
});
if (this.painting && this.action) {
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
}
};
Just replace it with the following. You dont need to modify the sketch.js file justy overwrite the redraw Prototype
In your code load the background and set the new redraw
const backgroundImage = new Image();
backgroundImage.src = "http://localhost/forms/img/vanImage.jpg";
// replace Sketch.js redraw function
Sketch.prototype.redraw = function(){
var sketch;
// dont need the next line use clear instead
// this.el.width = this.canvas.width();
const ctx = this.context = this.el.getContext('2d');
// clear canvas
this.context.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// If backgroundimage complete draw it first
if(backgroundImage && backgroundImage.complete){
ctx.drawImage(backgroundImage,0,0,ctx.canvas.width,ctx.canvas.height);
}
// back to the original code. :P
sketch = this;
$.each(this.actions, function() {
if (this.tool) {
return $.sketch.tools[this.tool].draw.call(sketch, this);
}
});
if (this.painting && this.action) {
return $.sketch.tools[this.action.tool].draw.call(sketch, this.action);
}
}

JavaScript Raphael, adding text on top of rectangle through different functions

I want to let function B work without having set the instruction code to function A. In praktical terms, to show text on top of a rectangle.
In this question Button A is used for creating the 'paper' and a rectangle(which uses the Raphael library). Button B for adding text on top of the rectangle. The HTML code looks like this:
<button onClick="func.A()">Func A</button>
<button onClick="func.B()">Func B</button>
The JavaScript code looks like this:
var func = (function functie($) {
return {
A: function() {
// Creates canvas 320 × 200 at 10, 50
var paper = Raphael(10, 50, 320, 200);
// Creates rectangle
var bg = paper.rect(0, 0, 320, 200);
// Sets the fill attribute of the circle to red (#f00)
bg.attr("fill", "#f00");
// Sets the stroke attribute of the circle to white
bg.attr("stroke", "#fff");
},
B: function() {
var t = paper.text(40, 15, "");
t.attr('text',"new text here");
t.attr();
};
})();
The problem is that when the instruction code of function B (var t = paper.text(40, 15, ""); and so on), are placed in function B, that the text i try to add won't be added to the rectangle.
If the instruction code of function B are placed in function A, then it will work, but this is not what i want. I want to let function B work without having set the instruction code to function A.
I hope this problem is clear enough to understand.
When you declare "var paper" in function A, that variable is local to function A. If you want to share state information between function calls, you have to store the state information in properties of your object, rather than local variables:
var func = (function functie($) {
return {
paper: null,
A: function() {
// Creates canvas 320 × 200 at 10, 50
this.paper = Raphael(10, 50, 320, 200);
// Creates rectangle
var bg = paper.rect(0, 0, 320, 200);
// Sets the fill attribute of the circle to red (#f00)
bg.attr("fill", "#f00");
// Sets the stroke attribute of the circle to white
bg.attr("stroke", "#fff");
},
B: function() {
var t = this.paper.text(40, 15, "");
t.attr('text',"new text here");
t.attr();
};
})();

Removing an image from html5 canvas when it's dragged to a certain location

I have an HTML5 canvas that is displaying a number of images and four description boxes. It is currently possible to drag and drop the images around the canvas, but I want to add functionality to remove an image when it is dragged to its correct description box.
I've tried writing the following function, but it does not currently seem to be doing anything... i.e. if I drag an image to its description box and drop it, it still remains on the canvas:
function initStage(images){
var stage = new Kinetic.Stage({
container: "container",
width: 1000,
height: 500
});
var descriptionLayer = new Kinetic.Layer();
//var imagesLayer = new Kinetic.Layer();
var allImages = [];
var currentScore = 0;
var descriptionBoxes = {
assetsDescriptionBox: {
x: 70,
y: 400
},
liabilitiesDescriptionBox: {
x: 300,
y: 400
},
incomeDescriptionBox: {
x: 530,
y: 400
},
expenditureDescriptionBox: {
x: 760,
y: 400
},
};
/*Code to detect whether image has been dragged to correct description box */
for (var key in sources){
/*Anonymous function to induce scope */
(function(){
var privateKey = key;
var imageSource = sources[key];
/*Check if image has been dragged to the correct box, and add it to that box's
array and remove from canvas if it has */
canvasImage.on("dragend", function(){
var descriptionBox = descriptionBoxes[privateKey];
if(!canvasImage.inRightPlace && isNearDescriptionBox(itemImage, descriptionBox)){
context.remove(canvasImage);
/*Will need to add a line in here to add the image to the box's array */
}
})
})();
}
}
The code I've written is based on the tutorial that I found at: http://www.html5canvastutorials.com/labs/html5-canvas-animals-on-the-beach-game-with-kineticjs/
Can anyone spot what I'm doing wrong, and how I can ensure that the image is removed from the canvas when it's dragged to its corresponding description box?
That example bugged me because it seemed old, so I edited it a little...
http://jsfiddle.net/LTq9C/1/
...keep in mind that I cant be positive that all my edits are the best way to do things, Im new and all ;)
And here I've edited it again for you to show the image being removed...
animal.on("dragend", function() {
var outline = outlines[privKey + "_black"];
if (!animal.inRightPlace && isNearOutline(animal, outline)) {
animal.remove();
animalLayer.draw();
}
});
http://jsfiddle.net/8S3Qq/1/

CodeMirror - Is it possible to scroll to a line so that it is in the middle of window?

I'm highlighting lines of HTML code in CodeMirror and I want to add an anchor that scroll CodeMirror editor to a given line.
I can scroll to a line X via setCursor method. But I would like to have the line X in the middle of CodeMirror window. Can I do that? I studied the API and demos but with no luck.
Thanks!
This one should work:
var editor = CodeMirror.fromTextArea(...);
function jumpToLine(i) {
var t = editor.charCoords({line: i, ch: 0}, "local").top;
var middleHeight = editor.getScrollerElement().offsetHeight / 2;
editor.scrollTo(null, t - middleHeight - 5);
}
It is very simple.
Init:
var editor = CodeMirror.fromTextArea(...);
If you want current position to set it later, you can use
editor.getScrollInfo();
It returns an JavaScript object:
{
"left": 0,
"top": 0,
"height": 500,
"width": 500,
"clientHeight": 300,
"clientWidth": 200
}
now you can set set editor scroll position using:
editor.scrollTo(left,top);
You want https://codemirror.net/doc/manual.html#scrollIntoView
Notice the optional margin parameter which should do what you want:
cm.scrollIntoView(what: {line, ch}|{left, top, right, bottom}|{from, to}|null, ?margin: number)
Your code would be something like:
cm.scrollIntoView({line:50, char:5}, 200)
Initialization:
var editor = CodeMirror.fromTextArea(...);
Function to show a line in the middle of editor:
function jumpToLine(i) {
// editor.getLineHandle does not help as it does not return the reference of line.
editor.setCursor(i);
window.setTimeout(function() {
editor.setLineClass(i, null, "center-me");
var line = $('.CodeMirror-lines .center-me');
var h = line.parent();
$('.CodeMirror-scroll').scrollTop(0).scrollTop(line.offset().top - $('.CodeMirror-scroll').offset().top - Math.round($('.CodeMirror-scroll').height()/2));
}, 200);
}

Categories

Resources