I made a square grid using HTML canvas and I am implementing a drag mechanism, such that the user can draw only rectangles by dragging over the grid.
In the below solution, the user can draw non-rectangle shapes, depending on how the user drags.
An additional question : The grid can be of any size and the drag operation is laggy for large rectangles. Any performance improvement suggestions for that ?
Here are my code of html and javascript
function getSquare(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: 1 + (evt.clientX - rect.left) - (evt.clientX - rect.left)%10,
y: 1 + (evt.clientY - rect.top) - (evt.clientY - rect.top)%10
};
}
function emptySquare(context) {
context.rect(0, 0, canvas.width, canvas.height);
context.fillStyle = "#ffffff"
context.fill();
context.strokeStyle = "#ddd";
context.stroke();
}
function range(start, end) {
var ans = [];
if (end > start) {
for (let i = start; i <= end; i += 10) {
ans.push(i);
}
} else {
emptySquare(context);
for (let i = start; i >= end; i -= 10) {
ans.push(i);
}}
return ans;
}
function drawBoard(context) {
for (var x = 0.5; x < 20001; x += 10) {
context.moveTo(x, 0);
context.lineTo(x,20000);
}
for (var y = 0.5; y < 20001; y += 10) {
context.moveTo(0, y);
context.lineTo(20000, y);
}
context.strokeStyle = "#ddd";
context.stroke();
}
function fillSquare(context, x, y){
context.fillStyle = "#70B7B5";
context.fillRect(x,y,9,9);
}
var canvas = document.getElementById('myBoard');
var context = canvas.getContext('2d');
var A;
drawBoard(context);
var isDrag= false;
// drawBoard(context);
canvas.addEventListener('mousedown', function(evt) {
var mousePos = getSquare(canvas, evt);
isDrag=true;
fillSquare(context, mousePos.x, mousePos.y);
previousPos = mousePos;
}, false);
canvas.addEventListener('mousemove', function(evt) {
if (isDrag) {
var mousePos = getSquare(canvas, evt);
var x_dist = range(previousPos.x, mousePos.x);
var y_dist = range(previousPos.y, mousePos.y);
for (x in x_dist) {
for (y in y_dist) {
fillSquare(context, x_dist[x], y_dist[y]);
}
}
}
}, false);
canvas.addEventListener('mouseup', function(evt) {
if (isDrag){
isDrag = false;
}
}, false);
var canvas = document.getElementById('myBoard');
var context = canvas.getContext('2d');
// drawBoard(context);
var isDrag=false;
canvas.addEventListener('mousedown', function(evt) {
var mousePos = getSquare(canvas, evt);
isDrag=true;
fillSquare(context, mousePos.x, mousePos.y);
previousPos = mousePos;
}, false);
canvas.addEventListener('mousemove', function(evt) {
if (isDrag) {
var mousePos = getSquare(canvas, evt);
var x_dist = range(previousPos.x, mousePos.x);
var y_dist = range(previousPos.y, mousePos.y);
for (x in x_dist) {
for (y in y_dist) {
fillSquare(context, x_dist[x], y_dist[y]);
}
}
}
}, false);
canvas.addEventListener('mouseup', function(evt) {
if (isDrag){
isDrag = false;
}
}, false);
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="./assets/css/style.css">
<meta charset="utf-8">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html">t</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link" aria-current="page" href="index.html">Home</a>
<a class="nav-link" href="#">About Us</a>
<a class="nav-link" href="Myblock.html">Mk</a>
</div>
<div class="navbar-nav ms-auto mb-2 mb-lg-0">
<a class="nav-link" href="login.html">Login</a>
</div>
</div>
</div>
</nav>
<div>
<div class="modal fade bd-example-modal-lg" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">간판 구입</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="" method="POST" enctype="multipart/form-data">
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<div class="preview-zone hidden">
<div class="box box-solid">
<div class="box-header with-border">
<div><b>미리보기</b></div>
<!-- <div class="box-tools pull-right">
<button type="button" class="btn btn-danger btn-xs remove-preview">
<i class="fa fa-times"></i> 초기화
</button>
</div> -->
</div>
<div class="box-body"></div>
</div>
</div>
<div class="dropzone-wrapper">
<div class="dropzone-desc">
<i class="glyphicon glyphicon-download-alt"></i>
<p>광고 이미지 선택 or 드래그해 옮겨 오세요.</p>
</div>
<input type="file" name="img_logo" class="dropzone">
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label for="exampleInputEmail1">닉네임/이름</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="닉네임/이름">
<label for="exampleInputEmail1">연락처</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="핸드폰 번호">
<label for="exampleInputEmail1">구매희망 면적</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="ex) 10 x 10 OR 100x100 ">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">닫기</button>
<button type="submit" class="btn btn-dark">적용하기</button>
</div>
</div>
</div>
</div>
<div>
<canvas id="myBoard" width="1440" height="1200"></canvas>
</div>
</hr>
<footer>
<p>© 2021 Nune Project</p>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="./assets/js/mainBoard.js"></script>
<script type="text/javascript" src="./assets/js/drag_field.js"></script>
</body>
The issue is being seen because range function is not clearing the squares when end > start. Clearing the squares in both the cases resolves the issue.
Here's the snippet that fixes the issue.
function range(start, end) {
var ans = [];
emptySquare(context);
if (end > start) {
for (let i = start; i <= end; i += 10) {
ans.push(i);
}
} else {
for (let i = start; i >= end; i -= 10) {
ans.push(i);
}
}
return ans;
}
The Lag Issue
The callback function attached to mousemove clears and draws squares on every invocation - a time consuming operation. Given that JavaScript is single threaded and this function is called at a very high rate, it might be the cause of the lag.
Debouncing the mousemove callback can help reducing the lag.
Another approach to solve the lag issue is to make two canvases: one for the background grid lines and the other one for drawing the green rectangles.
<div>
<canvas id="bg" width="1440" height="1200"></canvas>
<canvas id="myBoard" width="1440" height="1200"></canvas>
</div>
Set the #bg canvas so that #myBoard is in front of it:
#bg {
position: absolute;
z-index: -1;
}
Then draw the grid lines on the #bg canvas:
const bg = document.getElementById('bg')
const bgContext = bg.getContext('2d')
drawBoard(bgContext)
Finally, move the emptySquare(context) function into the mousemove event callback and edit it as follows:
function emptySquare(context) {
context.clearRect(0, 0, canvas.width, canvas.height)
}
So it will only clear the #myBoard canvas while #bg stays the same without needing to redraw it each time.
Example:
const bg = document.getElementById('bg')
const bgContext = bg.getContext('2d')
drawBoard(bgContext)
function getSquare(canvas, evt) {
var rect = canvas.getBoundingClientRect()
return {
x: 1 + (evt.clientX - rect.left) - ((evt.clientX - rect.left) % 10),
y: 1 + (evt.clientY - rect.top) - ((evt.clientY - rect.top) % 10),
}
}
function emptySquare(context) {
context.clearRect(0, 0, canvas.width, canvas.height)
}
function range(start, end) {
var ans = []
if (end > start) {
for (let i = start; i <= end; i += 10) {
ans.push(i)
}
} else {
for (let i = start; i >= end; i -= 10) {
ans.push(i)
}
}
return ans
}
function drawBoard(context) {
for (var x = 0.5; x < 20001; x += 10) {
context.moveTo(x, 0)
context.lineTo(x, 20000)
}
for (var y = 0.5; y < 20001; y += 10) {
context.moveTo(0, y)
context.lineTo(20000, y)
}
context.strokeStyle = '#ddd'
context.stroke()
}
function fillSquare(context, x, y) {
context.fillStyle = '#70B7B5'
context.fillRect(x, y, 9, 9)
}
var canvas = document.getElementById('myBoard')
var context = canvas.getContext('2d')
var isDrag = false
canvas.addEventListener('mousedown', function(evt) {
var mousePos = getSquare(canvas, evt)
isDrag = true
fillSquare(context, mousePos.x, mousePos.y)
previousPos = mousePos
}, false)
canvas.addEventListener('mousemove', function(evt) {
if (isDrag) {
var mousePos = getSquare(canvas, evt)
var x_dist = range(previousPos.x, mousePos.x)
var y_dist = range(previousPos.y, mousePos.y)
emptySquare(context)
for (x in x_dist) {
for (y in y_dist) {
fillSquare(context, x_dist[x], y_dist[y])
}
}
}
}, false)
canvas.addEventListener('mouseup', function(evt) {
if (isDrag) {
isDrag = false
}
}, false)
#bg {
position: absolute;
z-index: -1;
}
<div>
<canvas id="bg" width="1440" height="1200"></canvas>
<canvas id="myBoard" width="1440" height="1200"></canvas>
</div>
I am using the current version of Fabric JS (1.7.13) and I want some proper collision detection.
The Fabric JS website has this page: http://fabricjs.com/intersection
And there is a note at the top of the page:
Note that intersection is checked against bounding boxes, not actual
shapes
I am after a solution that does collision detection on the actual shapes.
Does such a solution exist?
I have found some similar questions here, like this one, but all of the ones I have found only do collision detection on the bounding boxes, not on the shapes themselves.
Maybe this code pen can help you- https://codepen.io/qbahamutp/pen/EPJmzj
/*
canvas dimension settings, using asset:
seiyria's bootstrap-slider
https://github.com/seiyria/bootstrap-slider
*/
var AdjustCanvasDimensions = function() {
window.canvas.setHeight(iHeight.getValue());
window.canvas.setWidth(iWidth.getValue());
window.canvas.renderAll();
$('label[for="canvas-width"]').text( iWidth.getValue() + 'cm' );
$('label[for="canvas-height"]').text( iHeight.getValue() + 'cm' );
console.log( 'Height:' + iHeight.getValue() );
// console.log( 'Width:' + iWidth.getValue() );
}
var iWidth = $('#canvas-width').slider().on('slide', AdjustCanvasDimensions).data('slider');
var iHeight= $("#canvas-height").slider({
// reversed : true
}).on('slide', AdjustCanvasDimensions).data('slider');
/*
canvas setup using asset:
fabricjs
https://github.com/kangax/fabric.js/
*/
window.canvas = new fabric.Canvas('fabriccanvas');
window.counter = 0;
var newleft = 0;
var edgedetection = 20; //pixels to snap
canvas.selection = false;
function plusrect(top, left, width, height, fill) {
window.canvas.add(new fabric.Rect({
top: 100,
name: 'rectangle ' + window.counter,
left: 0 + newleft,
width: 100,
height: 100,
fill: 'rgba(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ', 0.75)',
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
originX: 'left',
originY: 'top',
cornerSize: 15,
hasRotatingPoint: false,
perPixelTargetFind: true,
}));
window.counter++;
newleft += 200;
}
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords(); //Sets corner position coordinates based on current angle, width and height
canvas.forEachObject(function (targ) {
activeObject = canvas.getActiveObject();
if (targ === activeObject) return;
if (Math.abs(activeObject.oCoords.tr.x - targ.oCoords.tl.x) < edgedetection) {
activeObject.left = targ.left - activeObject.width;
}
if (Math.abs(activeObject.oCoords.tl.x - targ.oCoords.tr.x) < edgedetection) {
activeObject.left = targ.left + targ.width;
}
if (Math.abs(activeObject.oCoords.br.y - targ.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top - activeObject.height;
}
if (Math.abs(targ.oCoords.br.y - activeObject.oCoords.tr.y) < edgedetection) {
activeObject.top = targ.top + targ.height;
}
if (activeObject.intersectsWithObject(targ) && targ.intersectsWithObject(activeObject)) {
targ.strokeWidth = 10;
targ.stroke = 'red';
} else {
targ.strokeWidth = 0;
targ.stroke = false;
}
if (!activeObject.intersectsWithObject(targ)) {
activeObject.strokeWidth = 0;
activeObject.stroke = false;
}
});
});
$('#pushit').on('click', function(){
plusrect()
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.2.3/animate.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://cdn.rawgit.com/seiyria/bootstrap-slider/master/dist/css/bootstrap-slider.min.css" rel="stylesheet"/>
<script src="https://rawgithub.com/kangax/fabric.js/master/dist/fabric.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/seiyria/bootstrap-slider/master/dist/bootstrap-slider.min.js"></script>
<div id="start" class="container">
<form>
<fieldset>
<legend>Canvas eigenschappen</legend>
<div class="row">
<div class="form-group col-xs-9 col-xs-offset-2 col-md-offset-2">
<label for="canvas-width">Breedte</label>
<input id="canvas-width" name="canvas-width" data-slider-id='canvas-width' type="text" data-slider-min="100" data-slider-max="1000" data-slider-step="10" data-slider-value="500" style="width: 100%" />
</div>
</div>
<div class="row clearfix">
<div class="form-group col-xs-2 col-md-2 text-right">
<label for="canvas-height">Hoogte</label>
<input id="canvas-height" name="canvas-height" data-slider-id='canvas-height' type="text" data-slider-min="100" data-slider-max="400" data-slider-step="10" data-slider-value="200" data-slider-orientation="vertical" />
</div>
<div class="col-xs-9 canvaswrapper">
<canvas id="fabriccanvas" width="500" height="200" style="border:1px solid #ccc"></canvas>
</div>
</div>
<div class="row">
<div class="form-group col-xs-10">
<button type="button" id="pushit" class="btn btn-primary btn-lg">Object toevoegen </button>
</div>
</div>
</fieldset>
</form>
</div>
Hello People I'd Like to Know How to Draw More Arc Inside My Canvas, What I Need to Draw This circles:
So I Write This Responsive Code to Make my Canvas is Responsive:
var myCanvas = document.getElementById('myCanvas');
var ctx = myCanvas.getContext('2d');
var centerX = myCanvas.width / 2;
var centerY = myCanvas.height / 2;
var borderWidth = 20;
var borderColor = '#2DC36A';
var radius = (myCanvas.width / 2) - (borderWidth / 2);
// days arc canvas background
ctx.beginPath();
ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
ctx.lineWidth = borderWidth;
ctx.strokeStyle = borderColor;
ctx.stroke();
ctx.closePath();
// Make Canvas Responsive
function makeResponsive() {
var containerWidth = $('.progress-nested').width();
$('.progress-nested').height(containerWidth);
var canvasElements = ['#myCanvas'];
$('#myCanvas').width(containerWidth).height(containerWidth);
}
makeResponsive(); // Make Canvas Responsive in Document ready
$(window).resize(function () {
makeResponsive(); // Make Canvas Responsive in Window Resize
});
#progress {
width: 100%;
padding: 5px;
}
.progress-nested{
position:relative;
border-radius:50%;
}
canvas {
display:block;
position:absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div id="progress">
<div class="container">
<div class="row">
<div class="col-xs-3 col-md-2 col-md-offset-2">
<div class="progress-nested">
<canvas id="myCanvas" width="250" height="250"></canvas>
</div>
</div>
</div>
</div>
</div>
Note: Please Run Code Snippet In Full Page
I have made code for a bouncing ball (using the arc() method on a canvas). I am wondering how to make an image bounce around the screen. Here is my previous code (note that the launchBall() function is going to be the function that runs the bouncing image):
<!DOCTYPE HTML>
<html>
<body onload="startBall()" oncontextmenu="return false;" style="cursor: none; overflow: hidden;">
<center>
<div id="div" style="width: 1600px; height: 700px; border: 2px solid #000000; background-color: grey;">
<br>
<img draggable="false"id="image" style="position: absolute;" width="50" height="50"src="http://www.clker.com/cliparts/b/a/b/0/1207156679935151925noxin_crosshairs.svg.hi.png"/>
</div>
<center>
<p>Score: <a>0</a></p>
</center>
<script>
var dx = 3;
var dy = 3;
var x = 100;
var y = 100;
var radius = 20;
var interval;
function startBall(){
interval = setInterval(launchBall, 20);
}
function launchBall(){
var c = document.getElementById("canvas");
var ctx = c.getContext("2d");
ctx.clearRect(0, 0, 1275, 695);
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fillStyle = "red";
ctx.fill();
document.getElementById
if(x<20 || x>1255) dx=-dx;
if(y<20 || y>675) dy=-dy;
x = x + dx;
y = y + dy;
}
document.getElementById("div").onmousemove = function(e) {
document.getElementById("image").style.top = e.pageY -25 + "px";
document.getElementById("image").style.left = e.pageX -25 +"px";
}
</script>
</body>
</html>
I want a form with all elements aligned to the left. Everything is fine with google-chrome but with firefox the layout is defective, i.e. one input element (the range) is a little bit right. Is this a bug or did I miss something?
Fiddle
Chrome
Firefox
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script>
function changeEvas(evasValue) {
canvas = document.getElementById('smiley');
context = canvas.getContext('2d');
// face
context.beginPath();
context.arc(100, 100, 75, 0, 2 * Math.PI);
gradient = context.createRadialGradient(100, 100, 50, 75, 75, 100);
gradient.addColorStop(0, "yellow");
gradient.addColorStop(1, "orange");
context.fillStyle = gradient;
context.fill();
// left eye
context.beginPath();
context.arc(100 - 25, 75, 7, 0, 2 * Math.PI);
context.fillStyle = 'black';
context.fill();
// right eye
context.beginPath();
context.arc(100 + 25, 75, 7, 0, 2 * Math.PI);
context.fillStyle = 'black';
context.fill();
// mouth
context.beginPath();
context.moveTo(60, 125);
context.quadraticCurveTo(100, 162 - evasValue * 7.5, 140, 125);
context.lineCap = "round";
context.strokeStyle = 'black';
context.lineWidth = 4;
context.stroke();
}
</script>
</head>
<body>
<article>
<canvas id="smiley" width="200" height="200"></canvas>
<script>changeEvas(0);</script>
<form action="#" method="post" style="width: 200px;">
<label for="evas">Schmerzniveau</label>
<input name="evas" id="evas" type="range"
min="0" max="10" step="0.1" value="0"
style="width: 200px;"
onchange="changeEvas(this.value)"
onkeypress="changeEvas(this.value)"
onmousemove="changeEvas(this.value)"><br>
<img src="wedge.png" alt=""><br>
<input id="submit" name="submit" type="submit" value="Ok" style="width: 200px;">
</form>
</article>
</body>
</html>
set margin-left: 0; by your input:
<input name="evas" id="evas" type="range"
min="0" max="10" step="0.1" value="0"
style="width: 200px; margin-left: 0;"
onchange="changeEvas(this.value)"
onkeypress="changeEvas(this.value)"
onmousemove="changeEvas(this.value)"><br>