I am doing an image coloring web application with React and I am using html canvas for drawing. The problem is that the brush is sometimes doing these strange lines to coordinates where I didn't put the mouse. The mouse strokes I wanted to color are marked with the blue line, and the additional strokes that I don't want are circled with a yellow marker.
So my question is: How to solve the problem of the brush drawing additional strokes to points where I didn't put my mouse?
Linecap style:
Context.lineCap = "round"
The code for drawing is:
const drawMove = (clientX: number, clientY: number) => {
if (!isDrawing) {
return;
}
if (DrawCanvasRef.current) {
const Canvas = DrawCanvasRef.current;
const Context = Canvas.getContext("2d");
if (Context) {
const { x, y } = getMousePos(DrawCanvasRef, clientX, clientY);
if (x && y) {
Context.lineTo(x, y);
Context.stroke();
}
}
}
};
Other functions:
const startDrawing = (clientX: number, clientY: number) => {
if (DrawCanvasRef.current) {
const Canvas = DrawCanvasRef.current;
const Context = Canvas.getContext("2d");
if (Context) {
// Get current mouse positions
const { x, y } = getMousePos(DrawCanvasRef, clientX, clientY);
// If mouse position is allright, then begin line path
if (x && y) {
Context.beginPath();
Context.moveTo(x, y);
setIsDrawing(true);
}
}
}
};
const touchMove = (event: TouchEvent) => {
drawMove(
event.nativeEvent.touches[0].clientX,
event.nativeEvent.touches[0].clientY
);
};
const mouseMove = (event: MouseEvent) => {
drawMove(event.nativeEvent.clientX, event.nativeEvent.clientY);
};
const finishDrawing = () => {
if (DrawCanvasRef.current && HiddenCanvasRef.current) {
const Canvas = DrawCanvasRef.current;
const HiddenCanvas = HiddenCanvasRef.current;
const Context = Canvas.getContext("2d");
const HiddenContext = HiddenCanvas.getContext("2d");
if (Context && HiddenContext) {
// Stop drawing the path
Context.closePath();
setIsDrawing(false);
HiddenContext.drawImage(Canvas, 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
clearCanvas(DrawCanvasRef);
}
}
};
Related
`function Domain() {
mxCylinder.call(this);
}
/*
The next lines use an mxCylinder instance to augment the
prototype of the shape ("inheritance") and reset the
constructor to the topmost function of the c'tor chain.
*/
mxUtils.extend(Domain, mxCylinder);
Domain.prototype.redrawPath = function (
path: any,
x: any,
y: any,
w: any,
h: any
) {
var dy = this.extrude * this.scale;
var dx = this.extrude * this.scale;
path.moveTo(0, dy);
path.lineTo(dx, 0);
path.lineTo(w, 0);
path.lineTo(w, h - dy);
path.lineTo(w, h);
path.lineTo(0, h);
path.lineTo(0, dy);
path.lineTo(dx, 0);
path.close();
};
mxCellRenderer.registerShape("Domain", Domain);
export function main(container: any) {
if (!mxClient.isBrowserSupported()) {
mxUtils.error("Browser is not supported!", 200, false);
} else {
mxEvent.disableContextMenu(container);
var graph = new mxGraph(container);
graph.setCellsCloneable(true);
graph.setHtmlLabels(true);
graph.setPanning(true);
graph.setEnabled(false);
graph.centerZoom = false;
new mxRubberband(graph);
configureStylesheet(graph);
var parent = graph.getDefaultParent();
// Adds cells to the model in a single step
graph.getModel().beginUpdate();
try {
var v1 = graph.insertVertex(
parent,
null,
"RAN",
20,
20,
240,
120,
// 'image;image=icons/secure-gdpr-user-svgrepo-com.svg;selectable=0;connectable=0;editable=0;movable=0;fillColor=hsl(36deg 98% 51%)'
//here i'm regestering my shape how can i add an image and a label inside the shape "shape=Domain;startSize=30;fillColor=hsl(36deg 98% 51%);spacingLeft=10;align=left;fontColor=#FFFFFF;fontSize=18;shadow=0;strokeColor=none;whiteSpace=wrap;img;"
);
} finally {
// Updates the display
graph.getModel().endUpdate();
}
}
}
i Used configure style sheet to add the image to the shape but when i do this
// style[mxConstants.STYLE_SHAPE] = "Domain";
the image disappears and i get only the sape
export function configureStylesheet(graph: any) {
var style: any = new Object();
style = mxUtils.clone(style);
// style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter;
// style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL;
// style[mxConstants.STYLE_SHAPE] = "Domain";
style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_LABEL;
style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER;
style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP;
style[mxConstants.STYLE_IMAGE_ALIGN] = mxConstants.ALIGN_CENTER;
style[mxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = mxConstants.ALIGN_TOP;
style[mxConstants.STYLE_IMAGE] = "icons/secure-gdpr-user-svgrepo-com.svg";
style[mxConstants.STYLE_IMAGE_WIDTH] = "48";
style[mxConstants.STYLE_IMAGE_HEIGHT] = "48";
style[mxConstants.STYLE_SPACING_TOP] = "80";
style[mxConstants.STYLE_SPACING] = "8";
graph.getStylesheet().putCellStyle("img", style);
Here's the approach i'm trying to do but the thing is that the image is overwriting the shape and not able to display the image in my customised shape. i need to have that custom shape taking the label and the image and dynamically for it to be implemented more than once`
I draw 3 elements on my canvas and it works fine on my page (https://lodysreizen.nl/route2.html). But when I implement that page in a index page(https://lodysreizen.nl/ route2 button), where I load in into a div, only the outline that I draw in my html code are shown.
The other 2 lines and the dot don't show. What am I doing wrong?
I changed the order of the code(first script and then html and the other way around)
function draw() {
var canvas = document.getElementById('japan_canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.strokeStyle = "red";
<!--2 lijnen-->
ctx.beginPath();
ctx.moveTo(125, 125); <!--tekst-->
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.lineTo(63, 200);
ctx.lineTo(200, 210);
<!--ctx.closePath(); dit trekt een lijn vanaf het laatste punt naar het begin punt-->
ctx.lineWidth = 2; <!--dikte van de lijn-->
ctx.lineJoin = "round" <!--hoe de lijn er uitziek waar ze een knik maakt.-->
ctx.stroke();
<!--de circels-->
ctx.strokeStyle = "red"; <!--deze kleur werkt nog niet-->
ctx.beginPath();
ctx.arc(125, 45, 5, 0, 2 * Math.PI); <!--circel met centrumpunt 125,45)-->
ctx.fill(); <!--de circel vullen-->
<!--Path2D-->
}
}
canvas {
border: 1px solid black;
}
<body onload="draw();">
<canvas id="japan_canvas" width="300" height="300"></canvas></body>
function createPaint(parent) {
var canvas = elt('canvas', {});
var cx = canvas.getContext('2d');
var toolbar = elt('div', {class: 'toolbar'});
// calls the every function in controls, passing in context,
// then appending the returned results to the toolbar
for (var name in controls)
toolbar.appendChild(controls[name](cx));
var panel = elt('div', {class: 'picturepanel'}, canvas);
parent.appendChild(elt('div', null, panel, toolbar));
}
/************************************************************************
* helper functions
***********************************************************************/
// creates an element with a name and object (attributes)
// appends all further arguments it gets as child nodes
// string arguments create text nodes
// ex: elt('div', {class: 'foo'}, 'Hello, world!');
function elt(name, attributes) {
var node = document.createElement(name);
if (attributes) {
for (var attr in attributes)
if (attributes.hasOwnProperty(attr))
node.setAttribute(attr, attributes[attr]);
}
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
// if this argument is a string, create a text node
if (typeof child == 'string')
child = document.createTextNode(child);
node.appendChild(child);
}
return node;
}
// figures out canvas relative coordinates for accurate functionality
function relativePos(event, element) {
var rect = element.getBoundingClientRect();
return {x: Math.floor(event.clientX - rect.left),
y: Math.floor(event.clientY - rect.top)};
}
// registers and unregisters listeners for tools
function trackDrag(onMove, onEnd) {
function end(event) {
removeEventListener('mousemove', onMove);
removeEventListener('mouseup', end);
if (onEnd)
onEnd(event);
}
addEventListener('mousemove', onMove);
addEventListener('mouseup', end);
}
// loads an image from a URL and replaces the contents of the canvas
function loadImageURL(cx, url) {
var image = document.createElement('img');
image.addEventListener('load', function() {
var color = cx.fillStyle, size = cx.lineWidth;
cx.canvas.width = image.width;
cx.canvas.height = image.height;
cx.drawImage(image, 0, 0);
cx.fillStyle = color;
cx.strokeStyle = color;
cx.lineWidth = size;
});
image.src = url;
}
// used by tools.Spray
// randomly positions dots
function randomPointInRadius(radius) {
for (;;) {
var x = Math.random() * 2 - 1;
var y = Math.random() * 2 - 1;
// uses the Pythagorean theorem to test if a point is inside a circle
if (x * x + y * y <= 1)
return {x: x * radius, y: y * radius};
}
}
/************************************************************************
* controls
***********************************************************************/
// holds static methods to initialize the various controls;
// Object.create() is used to create a truly empty object
var controls = Object.create(null);
controls.tool = function(cx) {
var select = elt('select');
// populate the tools
for (var name in tools)
select.appendChild(elt('option', null, name));
// calls the particular method associated with the current tool
cx.canvas.addEventListener('mousedown', function(event) {
// is the left mouse button being pressed?
if (event.which == 1) {
// the event needs to be passed to the method to determine
// what the mouse is doing and where it is
tools[select.value](event, cx);
// don't select when user is clicking and dragging
event.preventDefault();
}
});
return elt('span', null, 'Tool: ', select);
};
// color module
controls.color = function(cx) {
var input = elt('input', {type: 'color'});
// on change, set the new color style for fill and stroke
input.addEventListener('change', function() {
cx.fillStyle = input.value;
cx.strokeStyle = input.value;
});
return elt('span', null, 'Color: ', input);
};
// save
controls.save = function(cx) {
// MUST open in a new window because of iframe security stuff
var link = elt('a', {href: 'currentTab'}, 'Save');
function update() {
try {
link.href = cx.canvas.toDataURL();
} catch(e) {
// some browsers choke on big data URLs
// also, if the server response doesn't include a header that tells the browser it
// can be used on other domains, the script won't be able to look at it;
// this is in order to prevent private information from leaking to a script;
// pixel data, data URL or otherwise, cannot be extracted from a "tainted canvas"
// and a SecurityError is thrown
if (e instanceof SecurityError)
link.href = 'javascript:alert(' +
JSON.stringify('Can\'t save: ' + e.toString()) + ')';
else
window.alert("Nope.");
throw e;
}
}
link.addEventListener('mouseover', update);
link.addEventListener('focus', update);
return link;
};
/************************************************************************
* tools
***********************************************************************/
// drawing tools
var tools = Object.create(null);
// line tool
// onEnd is for the erase function, which uses it to reset
// the globalCompositeOperation to source-over
tools.Line = function(event, cx, onEnd) {
cx.lineCap = 'round';
// mouse position relative to the canvas
var pos = relativePos(event, cx.canvas);
trackDrag(function(event) {
cx.beginPath();
// move to current mouse position
cx.moveTo(pos.x, pos.y);
// update mouse position
pos = relativePos(event, cx.canvas);
// line to updated mouse position
cx.lineTo(pos.x, pos.y);
// stroke the line
cx.stroke();
}, onEnd);
};
// erase tool
tools.Erase = function(event, cx) {
// globalCompositeOperation determines how drawing operations
// on a canvas affect what's already there
// 'destination-out' makes pixels transparent, 'erasing' them
// NOTE: this has been deprecated
cx.globalCompositeOperation = 'destination-out';
tools.Line(event, cx, function() {
cx.globalCompositeOperation = 'source-over';
});
};
// initialize the app
var appDiv = document.querySelector('#paint-app');
createPaint(appDiv);
canvas {
border: 1px solid black;
}
<div id="paint-app"></div>
I have a polygon with an array of coordinates , but i want to draw a point in each coordinates such as circle and fill the point with different color of poylgon
I try to fill the circle but i think it's not right
const drawPolygon = () => {
context.clearRect(0,0,cw,ch)
context.beginPath()
context.moveTo(coordinates[0].x, coordinates[0].y)
coordinates.forEach((item) => {
context.lineTo(item.x, item.y)
context.arc(item.x, item.y, 3, 0, Math.PI * 2, true)
})
context.closePath()
context.stroke()
context.fill()
}
Since you want to vary your fill colors, you'll need to call stroke() and fill() again for every new shape.
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const cw = canvas.width
const ch = canvas.height
const coordinates = [{x:20,y:20}, {x:270,y:40}, {x:230,y:130} , {x:50,y:90}]
const drawPolygon = () => {
context.clearRect(0,0,cw,ch)
// Draw polygon
context.fillStyle = 'rgba(255,0,0,.3)'
context.beginPath()
coordinates.forEach((item) => {
context.lineTo(item.x, item.y)
})
context.closePath()
context.stroke()
context.fill()
// Draw coordinate circles
context.fillStyle = 'rgba(0,0,255,.3)'
coordinates.forEach((item) => {
context.beginPath()
context.arc(item.x, item.y, 13, 0, Math.PI * 2, true)
context.stroke()
context.fill()
})
}
drawPolygon()
document.body.appendChild(canvas)
i am not getting the exact position of x and y co-ordinates in the ipad. Can some explain what am i doing wrong? this s a painting app for children where they can color on an image using canvas. its coloring but error being that its on starting on the point where the user touches the screen, its slightly below the point of contact. you can test this on your mobile or tablet..thie link is http://talentedash.co.uk/color/paint.html . Dont try this on desktop as it wont work
<script type="text/javascript">
var canvas = null; //canvas object
var context = null; //canvas's context object
var clearBtn = null; //clear button object
var defaultColor="#3577BB";
var defaultShape="round";
var defaultWidth=50;
/*boolean var to check if the touchstart event
is caused and then record the initial co-ordinate*/
var buttonDown = false;
//onLoad event register
window.addEventListener('load', initApp, false);
function initApp() {
setTimeout(function() { window.scrollTo(0, 1); }, 10); //hide the address bar of the browser.
canvas = document.getElementById('paintBox');
clearBtn = document.getElementById('clearBtn');
initializeEvents();
context = canvas.getContext('2d'); //get the 2D drawing context of the canvas
context.strokeStyle = defaultColor;
context.lineJoin = defaultShape;
context.lineWidth = defaultWidth;
/*Main image*/
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
main: 'transparent-charac1.png',
};
loadImages(sources, function(images) {
context.drawImage(images.main, 0, 0, 400, 800);
context.globalCompositeOperation = "destination-atop";
});
}
function initializeEvents() {
canvas.addEventListener('touchstart', startPaint, false);
canvas.addEventListener('touchmove', continuePaint, false);
canvas.addEventListener('touchend', stopPaint, false);
clearBtn.addEventListener('touchend', clearCanvas,false);
}
function clearCanvas() {
context.clearRect(0,0,canvas.width,canvas.height);
}
function startPaint(evt) {
if(!buttonDown)
{
context.beginPath();
context.moveTo(evt.touches[0].pageX, evt.touches[0].pageY);
buttonDown = true;
}
evt.preventDefault();
}
function continuePaint(evt) {
if(buttonDown)
{
context.lineTo(evt.touches[0].pageX ,evt.touches[0].pageY);
context.stroke();
}
}
function setColor(col)
{
context.strokeStyle = col;
}
function setSize(px)
{
context.lineWidth=px;
}
function stopPaint() {
buttonDown = false;
}
</script>
If I define a canvas and draw few shapes onto it, then how can I pinpoint each of the shape or image so as to declare events and other properties on the each shape. Consider I have a Rectangle and a triangle. SO can I have some mechanism so as to define them as specific entity and can I deal with them individually. I know about KineticJS but I would like to implement this functionality on my own(For learning purpose). Can anyone pinpoint the way to do it. Or may be an algorithmic approach??
I would like explain pinpoint using mouse events
First of all you have to implement a method to get mouse position
function getMousePos(canvas, evt){
// get canvas position
var obj = canvas;
wrapper = document.getElementById('wrapper');
var top = 0;
var left = 0;
while (obj && obj.tagName != 'BODY') {
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
// return relative mouse position
var mouseX = evt.clientX - left + window.pageXOffset+wrapper.scrollLeft;
var mouseY = evt.clientY - top + window.pageYOffset+wrapper.scrollTop;
return {
x: mouseX,
y: mouseY
};
}
Rectangle
Say, we have a rectangle with following values x1, y1, w, h
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(mouseX>x1 && mouseX<x1+w && mouseY>y1 && mouseY<y1+h)
{
alert('mouse on rect')
}
});
Circle
Say, we have a circle with following values x, y, r
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(Math.pow(mouseX-x,2)+Math.pow(mouseY-y,2)<Math.pow(r,2))
{
alert('mouse on circle')
}
});
By this way we can pinpoint a object of canvas
You can't use any existing functionality in the DOM for that. So you have to write it yourself. You could start by making an object model like this:
function Shape(x, y) {
var obj = {};
obj.x = x;
obj.y = y;
obj.paint = function(canvasTarget) {
}
return obj;
}
function Rectangle(x, y, width, height) {
var obj = Shape(x, y);
obj.width = width;
obj.height = height;
obj.paint = function(canvasTarget) {
//paint rectangle on the canvas
}
return obj;
}
function Canvas(target) {
var obj = {};
obj.target = target
obj.shapes = [];
obj.paint = function() {
//loop through shapes and call paint
}
obj.addShape(shape) {
this.shapes.push(shape);
}
}
After making the object model you could use it to draw the shapes like this:
var cnv = new Canvas();
cnv.addShape(new Rectangle(10,10,100,100));
cnv.paint();
Then you can handle the onclick event on the canvas and determine which shape is clicked on.