Skip to content

Instantly share code, notes, and snippets.

@HMilbradt
Last active March 26, 2023 19:04
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 HMilbradt/85784458f9ef4d97ca5aca5ec147a586 to your computer and use it in GitHub Desktop.
Save HMilbradt/85784458f9ef4d97ca5aca5ec147a586 to your computer and use it in GitHub Desktop.
A simple pen drawing example using HTML canvas, completely from scratch. Created as part of my blog https://harrisonmilbradt.com/articles/canvas-line-drawing-explorations
<html>
<body>
<div class="relative">
<canvas width="500" height="500" id="canvas"></canvas>
<button id="blue-button">Blue</button>
<button id="red-button">Red</button>
<input type="range" id="pen-width" min="1" max="10" value="5" />
<div id="width-indicator"
style="position: absolute; border: 2px solid black; border-radius: 50%; width: 5px; height: 5px; pointer-events: none">
</div>
</div>
<script>
// Read here: https://harrisonmilbradt.com/articles/canvas-line-drawing-explorations
const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
let penColor = 'red'
let penWidth = 5
// Your code here 👇
let previousX = 0
let previousY = 0
const drawPixels = (x, y) => {
ctx.beginPath()
ctx.arc(x, y, penWidth, 2 * Math.PI, false)
ctx.fillStyle = penColor
ctx.fill()
}
const erasePixels = (x, y) => {
ctx.save()
ctx.beginPath()
ctx.arc(x, y, penWidth, 2 * Math.PI, false)
ctx.globalCompositeOperation = 'destination-out'
ctx.fillStyle = 'white'
ctx.fill()
ctx.restore()
}
const drawLine = (x, y) => {
ctx.beginPath()
ctx.moveTo(previousX, previousY)
ctx.lineTo(x, y)
ctx.strokeStyle = penColor
ctx.lineWidth = penWidth
ctx.lineCap = 'round'
ctx.stroke()
}
const eraseLine = (x, y) => {
ctx.save()
ctx.beginPath()
ctx.moveTo(previousX, previousY)
ctx.lineTo(x, y)
ctx.globalCompositeOperation = 'destination-out'
ctx.strokeStyle = 'white'
ctx.lineWidth = penWidth
ctx.lineCap = 'round'
ctx.stroke()
ctx.restore()
}
const render = (e) => {
const x = e.offsetX
const y = e.offsetY
console.log(`Drawing at (${x}, ${y})`)
if (e.shiftKey) {
// erasePixels(x, y)
eraseLine(x, y)
} else {
// drawPixels(x, y)
drawLine(x, y)
}
previousX = x
previousY = y
}
const blueButton = document.getElementById('blue-button')
blueButton.addEventListener('click', () => {
penColor = 'blue'
})
const redButton = document.getElementById('red-button')
redButton.addEventListener('click', () => {
penColor = 'red'
})
const widthIndicator = document.getElementById('width-indicator')
const penWidthInput = document.getElementById('pen-width')
penWidthInput.addEventListener('input', (e) => {
penWidth = parseInt(e.target.value)
// New code in this function 👇
widthIndicator.style.width = `${penWidth}px`
widthIndicator.style.height = `${penWidth}px`
})
canvas.addEventListener('mousemove', (e) => {
const x = e.pageX
const y = e.pageY
widthIndicator.style.left = `${x - penWidth / 2 - 2}px`
widthIndicator.style.top = `${y - penWidth / 2 - 2}px`
})
// End of your code 👆
canvas.addEventListener('mousedown', (e) => {
previousX = e.offsetX
previousY = e.offsetY
canvas.addEventListener('mousemove', render)
canvas.addEventListener('mouseup', () => {
canvas.removeEventListener('mousemove', render)
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment