Skip to content

Instantly share code, notes, and snippets.

@seangeleno
Created December 18, 2022 02:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seangeleno/0cbb0bd3c18c1aaff08e62f9dc519bc9 to your computer and use it in GitHub Desktop.
Save seangeleno/0cbb0bd3c18c1aaff08e62f9dc519bc9 to your computer and use it in GitHub Desktop.
Canvas Drawing App
<body>
<div class="controls">
<h3>Shape:</h3>
<div class='lightBorder' >
<p><input type="radio" name="shape" value="line" checked="checked"> Line</p>
<p><input type="radio" name="shape" value="rect"> Rectangle</p>
<p><input type="radio" name="shape" value="circle"> Circle</p>
<p><input type="radio" name="shape" value="square"> Square</p>
<p><input type="radio" name="shape" value="ellipse"> Ellipse</p>
<p><input type="radio" name="shape" value="polygon"> Polygon</p>
</div>
<h3>Line Type:</h3>
<div class='lightBorder'>
<p><label>Line Width: <input id='lineWidth' type='range' step='1' min="1" max="200" value="4"></label></p>
Line End:
<p><input name="lineCap" value="round" type='radio' checked="checked">round</p>
<p><input name="lineCap" value="square" type='radio'>square</p>
<p><input name="lineCap" value="butt" type='radio'>butt</p>
</div>
<h3>Colors:</h3>
<div class='lightBorder'>
<!-- color -->
<p><label>Fill Color: <input id='fillColor' type='color' step='1' value="#24B0D5"></label></p>
<p><label>Stroke Color: <input id='strokeColor' type='color' step='1'> </label></p>
<p><label>Background Color:<input id='backgroundColor' type='color' step='1' value="#ffffff"></label></p>
*changing background will delete image!
</div>
<div class='lightBorder'>
<!-- polygon sides -->
<p><label>Polygon Sides: <input id='polygonSides' type='range' step='1' min="3" max="12"></label></p>
</div>
<form>
<h3>Input Text:</h3> <input type="text" id="textInput"/>
</form>
<!-- fill or no fill, you decide -->
<p><label>Fill: <input id='fillBox' type='checkbox' checked='checked'></label></p>
<p><label style="font-size:12px;">XOR: <input id='xor' type='checkbox'></label></p>
<button type="button" class="button-error pure-button" id="clearCanvas" name="button">Clear Canvas</button>
</div>
<canvas id="canvas" width="650" height="800"></canvas>
</body>
var canvas,
context,
dragging = false, //this will be dragging if mouse move is followed by mouse down
dragStartLocation,
snapshot;
//getBoundingClientRect
//The returned value is a DOMRect object, which contains read-only left, top, right and
//bottom properties describing the border-box in pixels. top and left are relative to the top-left of the viewport.
// rect is a DOMRect object with six properties: left, top, right, bottom, width, height
//var rect = obj.getBoundingClientRect();
//define mouse coordinates
function getCanvasCoordinates(event) {
var x = event.clientX - canvas.getBoundingClientRect().left;
var y = event.clientY - canvas.getBoundingClientRect().top;
//return object where x is x and y is y
return {x: x, y: y};
}
//this avoids dragging the image
//The getImageData() method returns an ImageData object that copies the pixel data for the specified rectangle on a canvas.
function takeSnapShot() {
snapshot = context.getImageData(0, 0, canvas.width, canvas.height);
}
//These must be added to dragStart()
function restoreSnapShot() {
context.putImageData(snapshot, 0, 0);
}
function drawLine(position) {
context.beginPath();
context.moveTo(dragStartLocation.x, dragStartLocation.y);//this will be the first point and during mouse up the line will be drawn
context.lineTo(position.x, position.y); //current position of x and y during mouseMove event
context.stroke(); // The stroke() method actually draws the path you have defined with all those moveTo() and lineTo() methods. So Cool!
}
//The arc() method creates an arc/curve (used to create circles, or parts of circles). To create a circles set start angle to 0 and end angle to 2*Math.PI
function drawCircle(position) { //takes position during mouse up
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
context.beginPath();
context.arc(dragStartLocation.x, dragStartLocation.y, radius, 0, 2 * Math.PI);
// context.fill();
}
function drawEllipse(position) {
var w = position.x - dragStartLocation.x ;
var h = position.y - dragStartLocation.y ;
var radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2));
context.beginPath();
//Used the .ellipse method instead of arc to give an extra radius, radius x and radius
var cw = (dragStartLocation.x > position.x) ? true : false;
console.log(cw);
context.ellipse(dragStartLocation.x, dragStartLocation.y, Math.abs(w), Math.abs(h), 0, 2 * Math.PI, false);
}
function drawRect(position) {
console.log(position.x, dragStartLocation.x);
var w = position.x - dragStartLocation.x ;
var h = position.y - dragStartLocation.y ;
context.beginPath();
context.rect(dragStartLocation.x, dragStartLocation.y, w, h);
}
function drawPolygon(position, sides, angle) {
var coordinates = [],
radius = Math.sqrt(Math.pow((dragStartLocation.x - position.x), 2) + Math.pow((dragStartLocation.y - position.y), 2)),
index = 0;
for (index; index < sides; index++) {
coordinates.push({
x: dragStartLocation.x + radius * Math.cos(angle),
y: dragStartLocation.y - radius * Math.sin(angle)
})
angle += (2 * Math.PI) / sides;
}
context.beginPath();
context.moveTo(coordinates[0].x, coordinates[0].y);
for (index = 0; index < sides; index++) {
context.lineTo(coordinates[index].x, coordinates[index].y);
}
context.closePath();
// context.fill();
}
function draw(position) {
var fillBox = document.getElementById("fillBox")
, shape = document.querySelector('input[type="radio"][name="shape"]:checked').value
, polygonSides = document.getElementById('polygonSides').value
// , polygonAngle = document.getElementById('polygonAngle').value
, polygonAngle = calculateAngle(dragStartLocation, position)
, lineCap = document.querySelector('input[type="radio"][name="lineCap"]:checked').value
, writeCanvas = document.getElementById('textInput').value
, xor = document.getElementById('xor');
//global context
context.lineCap = lineCap;
//we don't need even't handlers because before drawing we are jsut taking a default value
if (shape === "circle") {
drawCircle(position);
}
if (shape === "square") {
drawPolygon(position, 4, Math.PI / 4);
}
if (shape === "line") {
drawLine(position);
}
if (shape === "ellipse") {
drawEllipse(position);
}
if (shape === "rect") {
drawRect(position);
}
if (shape === "polygon") {
drawPolygon(position, polygonSides, polygonAngle * (Math.PI / 180));
}
if (xor.checked){
context.globalCompositeOperation = "xor";
} else {
context.globalCompositeOperation = "source-over";
}
if (fillBox.checked) {
context.fill();
} else {
context.stroke();
}
}
//define dragstart, drag and dragStop
function dragStart(event) {
dragging = true;
dragStartLocation = getCanvasCoordinates(event);
takeSnapShot();
}
function calculateAngle(start, current) {
var angle = 360 - Math.atan2(current.y - start.y, current.x - start.x) * 180 / Math.PI;
return angle;
}
function drag(event) {
var position;
if (dragging === true) {
restoreSnapShot();
position = getCanvasCoordinates(event);
//generic
draw(position);
}
}
//Drag Stop
function dragStop(event) {
dragging = false; //dragging stops here
restoreSnapShot();
var position = getCanvasCoordinates(event);
//generic
draw(position);
}
//Line Width Here
function changeLineWidth() {
context.lineWidth = this.value;
//**important**
//event.stopPropagation() prevents the vent from bubblim up the DOM tree, preventing any parent handlers from being notified of the event.
event.stopPropagation();
}
//Fill Color
function changeFillStyle() {
context.fillStyle = this.value;
event.stopPropagation();
}
//Stroke Color
function changeStrokeStyle() {
context.strokeStyle = this.value;
event.stopPropagation();
}
//backgroundColor
function changeBackgroundColor() {
context.save();
context.fillStyle = document.getElementById('backgroundColor').value;
context.fillRect(0, 0, canvas.width, canvas.height);
context.restore();
//more info on save() and resote() www.html5.litten.com/understanding-save-and-restore-for-the-canvas-context/
}
function eraseCanvas() {
context.clearRect(0, 0, canvas.width, canvas.height);
}
//write on canvas
function writeCanvas() {
context.font = '55px Impact';
context.fillText(textInput.value, 25, 175);
console.log(textInput.value);
}
//function invoked when document is fully loaded
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
var lineWidth = document.getElementById('lineWidth'),
fillColor = document.getElementById('fillColor'),
strokeColor = document.getElementById('strokeColor'),
canvasColor = document.getElementById('backgroundColor'),
clearCanvas = document.getElementById('clearCanvas'),
textInput = document.getElementById('textInput');
// context.strokeStyle = 'rebeccapurple';
// context.font = '25px Impact';
// context.fillText(writeCanvas, 50, 150);
// var imgElement = document.getElementById("logo");
// context.fillStyle = context.createPattern(imgElement, 'repeat');
context.lineWidth = lineWidth.value;
// no need for linecap will be defined directly by user
// context.lineCap = 'square';
context.fillStyle = fillColor.value;
//shapes made transparent by overlapping shapes
// context.globalCompositeOperation = 'xor';
//event listeners below
canvas.addEventListener('mousedown', dragStart, false);
canvas.addEventListener('mousemove', drag, false);
canvas.addEventListener('mouseup', dragStop, false);
lineWidth.addEventListener('input', changeLineWidth, false);
fillColor.addEventListener('input', changeFillStyle, false);
strokeColor.addEventListener('input', changeStrokeStyle, false);
canvasColor.addEventListener('input', changeBackgroundColor, false);
clearCanvas.addEventListener('click', eraseCanvas, false);
textInput.addEventListener('input', writeCanvas, false);
}
window.addEventListener('load', init, false);
canvas {
float: left;
position: relative;
border-radius: 10px;
margin: 10px;
border: 1px solid gray;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#canvas1 {
margin-left: 1150px;
margin-top: 30px;
}
.controls {
width: 200px;
padding: 12px;
border: 1px solid gray;
border-radius: 10px;
background: #D9D9D9;
transition: all .5s ease;
float: left;
position: relative;
margin: 10px;
height: 800px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
font-size: 13px;
font-family: Arial;
}
.controls h3 {
color: #3D3D3D;
text-transform: uppercase;
font-size: 11px;
line-height: 10px;
}
.lightBorder {
border: 1px solid #B6B6B6;
margin-top: 10px;
margin-bottom: 18px;
padding: 5px;
border-radius: 7px;
background: #EEEEEE;
box-shadow: inset 1px 1px 10px rgba(0,0,0,0.1);
}
}
h1 {
margin-left: 300px;
}
p {
margin: 4px;
}
/*body {
background: #FF4E50;
background: linear-gradient(to left, #FF4E50 , #F9D423);
transition: all .5s ease;
}*/
.button-success,
.button-error,
.button-warning,
.button-secondary {
color: white;
border-radius: 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.button-success {
background: rgb(28, 184, 65); /* this is a green */
}
.button-error {
background: rgb(202, 60, 60); /* this is a maroon */
}
.button-warning {
background: rgb(223, 117, 20); /* this is an orange */
}
.button-secondary {
background: rgb(66, 184, 221); /* this is a light blue */
}
<link href="https://yui.yahooapis.com/pure/0.6.0/pure-min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment