JCanvas: Erasing Canvas doesn't erase draggable elements - javascript

I wanted to enhance the drawing application I built with JS by allowing the user to draw various shapes. I want to make it convenient for the users by allowing them to drag these shapes around. So I used jCanvas, since it offers a simple way of dragging a shape:
$("#can").drawRect({
draggable: true,
fillStyle: "#000",
width: 100, height: 100,
x: 100,
y: 100,
});
The problem is, I need a function to erase it again. So I made this script:
<script>
$("#can").drawRect({
draggable: true,
fillStyle: "#000",
width: 100, height: 100,
x: 100,
y: 100,
});
$("#erase").click(function () {
$("#can").clearCanvas();
});
</script>
<canvas id="can" width="200px" height="150px"></canvas>
<button id="erase">Clear Canvas</button>
This doesn't work though. When I erase, it clears out everything at first, but when I try to draw on the Canvas, the shape appears again.
I checked the code of jCanvas, and it appears that it just draws tons of rectangles when dragged, and clears them away to create the illusion of the shape being dragged. But when this happens, the cleared Canvas retreats itself and the shape is visible again.
Is there a way to clear the shape away for eternity? Or do I have to reload the page for that to work?
Thanks in advance

You are not using Layer explicitly, but when you set drag property the Canvas create a layer:
Layers can be made draggable using the draggable property.
https://projects.calebevans.me/jcanvas/docs/draggableLayers/
So, clearCanvas is not suitable:
This method is not meant to be used if you are using the jCanvas Layer API, because the API handles redrawing for you in many cases, and so if you try to clear the canvas. you layers will eventually be redrawn by jCanvas when it deems necessary.
You need remove the layer:
$('canvas').removeLayer(0);
or
$('canvas').removeLayers();
Look more options in Doc: https://projects.calebevans.me/jcanvas/docs/manipulateLayers/
If this help you, check my answer as correct or voteup!

Related

Is it possible to implement a Paint Bucket using Konva.js?

I’ve managed to implement, using konva, multiple tools that allow users to draw different shapes and patterns like: rectangle, circle, arrows, free draw, eraser, et. c.
I’m trying to achieve something like: using a paint bucket, users should be able to fill different parts of a shape, if over that shape are drawn other shapes or patterns.
Maybe this use case helps to understand better my question:
The user draws a circle.
Afterwards he draws lines over that circle so will be split in multiple areas.
The user uses now the paint bucket and tries to fill only the areas of that circle.
I’m wondering if, using konva, is possible to achieve this functionality.
Until now I've manage only to fill entire shapes, similar to this.
Update
Added images for the use case above.
1 & 2. User draws a circle and lines over it:
Using paint bucket user can fill certain areas of that circle:
Any feedback will be very welcomed.
Bad news: What you want cannot be done with Konvajs, as it is designed to work with vectorial images. Each figure is created as a whole by an equation and is "separated" of other figures (as the lines X and Y and the circle are separate in the Snippet below. It is not a raster layer. To do a paint bucket tool in vector graphics is hard.
(See Good news at the end!)
var width = window.innerWidth;
var height = window.innerHeight;
var stage = new Konva.Stage({
container: 'container',
width: width,
height: height
});
var layer = new Konva.Layer();
var circle = new Konva.Circle({
x: 180,
y: 120,
radius: 50,
fill: 'red',
stroke: 'black',
strokeWidth: 4
});
var lineX = new Konva.Line({
x: 180, // 180-50
y: 120,
points: [-100, 0, 100, 0],
stroke: 'black',
strokeWidth: 4
});
var lineY = new Konva.Line({
x: 180, // 180-50
y: 120,
points: [0, -100, 0, 100],
stroke: 'black',
strokeWidth: 4
});
circle.on('click', function() {
var fill = this.fill() == 'red' ? '#00d00f' : 'red';
this.fill(fill);
layer.draw();
});
layer.add(circle);
layer.add(lineX);
layer.add(lineY);
stage.add(layer);
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #f0f0f0;
}
<script src="https://unpkg.com/konva#3.2.6/konva.min.js"></script>
<div id="container"></div>
Good news: But you can do it with Canvas, HTML5 and Javascript.
Here you have a good tutorial which includes a DEMO (on top of the page) and the SOURCE CODE to Create a Paint Bucket Tool in HTML5 and JavaScript
Hope this helps you!
Unless konva has a specific implementation for it that I don’t know of, this is more of an algorithmic problem.
One approach you could take if you decide to implement it on your own is something like a cell automaton. You would create one pixel somewhere in the middle, and it would grow over time (of course you don’t need to show the growth). The rules for it would be that any pixel of the specified color must colorize any pixel around it if it is the same as the average color of pixels around original point (where you clicked to fill color).
Hope this helps :)
I’ve came up with a solution: https://codesandbox.io/s/stupefied-northcutt-1y0cg.
In short, what this solution does is that when the stage is mounted, the paint bucket is setup targeting the canvas generated by konva. The pixels around the one clicked are colored using a cell automaton algorithm, as per Antoni's suggestion.
Okay, but the downside of this approach is that whenever you’re drawing a shape after paint bucket is used, the paint bucket changes get lost because (I assume) render() doesn’t know about the "vanilla" changes made in setupPaintBucket().
Another downside of this approach is that the canvas is blurry.
Sources:
Draw circle, arrow and free hand: https://codesandbox.io/s/43wzzv0l37
Vanilla Paint Program: https://codepen.io/falldowngoboone/pen/zxRXjL

Crop or Clip image to Circle using JointJS

I'm very new to both JointJS and SVG, so I am having a hard time interpreting some of the documentation for JointJS. I am trying to get randomly searched images to display in a circle for this project, in a graph that uses JointJS to display and link the images with arrows. I am convinced this shouldn't be as hard as I am making it, but I have spent hours going through the documentation, so I need help. From what I can tell, joint.shapes.standard.Circle elements cannot take an image as a background or fill. I would be really happy to use the .BorderedImage elements, but when I change the radius to make them a circle, the border changes without cropping the image. I also tried to use the .Image element and change the radius, and again the image didn't change. I am under the impression that I could add a clipPath definition to an .Image or .BorderedImage to force a circle, but I am not familiar enough with the syntax to be clear on where to do that, and none of the examples I have found on stackoverflow have given me enough information to do that. I tried using the .define method, and kept getting "cannot read property define of undefined" errors, so I know I wasn't using that right.
Here's what I have in the way of error-free code (rounded border that doesn't crop):
var graph = new joint.dia.Graph;
var paper = new joint.dia.Paper({
el: document.getElementById('myholder'),
model: graph,
width: 600,
height: 600,
gridSize: 1,
// makes code not draggable
interactive: { elementMove: false, arrowheadMove: false }
});
var borderedImage = new joint.shapes.standard.BorderedImage();
borderedImage.resize(100, 100);
borderedImage.position(100, 100);
borderedImage.attr('root/title', 'joint.shapes.standard.BoarderedImage');
borderedImage.attr('label/text', 'Monster\nImage');
borderedImage.attr('border/rx', "50%");
borderedImage.attr('image/xlinkHref', './assets/images/monster.svg');
borderedImage.addTo(graph);
The code I tried for .define looked like:
joint.shapes.Element.define('standard.BorderedImage', {
attrs: {
body: {
rx: "50%"
}
}
})
Some of the resources I have looked at and couldn't figure out how to use correctly are:
Putting multiple films in a circle in Raphael/Joint.js
How to set background image for a rectangle in JointJs?
Using predefined SVG file for creating a custom JointJS shape with ports
https://resources.jointjs.com/docs/jointjs/v2.1/joint.html#dia.Paper.prototype.properties

How would I go about making a custom (very weirdly shaped progress bar) in HTML?

Would it be possible to create (in effect, a progress bar) in HTML, or any type of method in HTML that can emulate the effect of a progress bar by having a track fill up to a certain point? I will also need to be able to control to what % the track fills up to. See image example: http://i.stack.imgur.com/x1Do2.jpg
Note: the parts where the track curves will have curved edges.
This could be simply animated in flash, however, for other purposes this needs to be done strictly using HTML5 or Javascript. Would this be possible?
I can't even begin to imagine how to go about achieving this.
See sonic.js and demo
You define paths and then display it. This works as a loading gif using canvas but you should be able to modify the js to display it as a progress bar.
var square = new Sonic({
width: 100,
height: 100,
fillColor: '#000',
path: [
['line', 10, 10, 90, 10],
['line', 90, 10, 90, 90],
['line', 90, 90, 10, 90],
['line', 10, 90, 10, 10]
]
});
square.play();
document.body.appendChild(square.canvas);
Yes it would be possible to do this in html and javascript.
There are a lot of ways to achieve this.
The simplest being 100 images 1 for each percent complete.
Another way would be to have a lot of divs with a fixed size,position and back ground color, then toggle there visibility.
A third was would be to use a canvas element and draw onto the canvas as progress happened.
If you needed a forth way you could use SVG to achieve this effect.

RaphaelJS: Move and scale objects with FreeTransform in limits

I am using the FreeTransform plugin for moving and scaling objects.
I want to limit this moving and scaling to the size of another rect which contains this object. How can I achieve this?
I have provided an example of problem on JSFiddle. (I want the red rect to stay inside the other rect.)
Thank you.
The FreeTransform plugin provides the option to set a boundary:
boundary: { x: x-pos, y: y-post, width: i, height: i},
see the documentation
https://github.com/ElbertF/Raphael.FreeTransform/

KineticJS strokeWidth of 1 causes a blurred, semi-opaque line instead of a sharp 1 pixel line

I've looked around the internet and found nothing, I've looked on other KineticJS examples that use a strokeWidth of 1 on their rectangles and they all appear to have a semi-opaque 2 pixel line rather than a nice sharp 1px opaque black line.
Now, I am guessing that as Google has nothing that the solution is either really simple or impossible, but.. do you know how I can get a one px border using KineticJS?
$(window).load(function(){
var stage = new Kinetic.Stage({container: "kineticdiv", width: 700, height: 400});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect({
x: stage.attrs.width/2, y: stage.attrs.height/2,
width: 100, height: 100,
fill: "#eee", stroke: "black", strokeWidth: 1
});
layer.add(rect);
stage.add(layer);
});
Anyone got any ideas?
when you draw a line from (x,y1) to (x,y2) (say; the same is true for horizontal lines) you need to worry about whether x is "in the middle of a pixel". if the line is "between pixels" then it will be half in one and half in another. the result will look blurred (it's basically anti-aliasing).
graphics systems vary on whether coordinates are for corners or centres, but you can fix the issue by experimenting a little - you just need to add half a pixel width to the coord and try again.
in the case of an html5 canvas (0,0) is the top left corner, so if you have no transform i guess the top left pixel centre is at (0.5, 0.5).
Another approach: if you use Integer numbers as coordinates and ortogonal 1px weight lines, then you can move the whole stage by [0.5, 0.5] and you dont have to add the half of a pixel to each coordinate, you can then use Integer numbers as coordinate as your whole stage will be moved half of pixel to right and the same to down.
There is a cool approach to get exactly what you want: group two similar shapes. The one at the lower level is one pixel larger then the one at the top. Fill the bottom one with the color you want your border (in your case: Black). works fine for me and has the precision and quality of CSS
The easiest way of solving this with Kinetic is to use the offset properties. So, rather than shifting individual coordinates of what you're drawing, your entire line/shape/group/layer/stage is offset by that much, theoretically getting it where you want it with minimum fuss:
var rect = new Kinetic.Rect({
x: stage.attrs.width/2, y: stage.attrs.height/2,
width: 100, height: 100,
fill: "#eee", stroke: "black", strokeWidth: 1,
offsetX: 0.5,
offsetY: 0.5
});
or, to get a whole bunch of stuff at once:
var layer = new Kinetic.Layer({
offsetX: 0.5,
offsetY: 0.5
});
That said, not all items benefit from this trick. Some, in fact, get fuzzier. So, make sure to apply the offset at the most atomic level that avoids contaminating shapes that don't benefit from it.

Categories