Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 12 additions & 94 deletions app/javascript/draw.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SignaturePad from 'signature_pad'
import { cropCanvasAndExportToPNG } from './submission_form/crop_canvas'
import { setupCanvasSizing } from './submission_form/canvas_sizing'
import { isValidSignatureCanvas } from './submission_form/validate_signature'

window.customElements.define('draw-signature', class extends HTMLElement {
Expand Down Expand Up @@ -46,103 +47,20 @@ window.customElements.define('draw-signature', class extends HTMLElement {
})
})

this.setupCanvasSizing()
this.canvasSizing = setupCanvasSizing({
getCanvas: () => this.canvas,
getPad: () => this.pad,
scale: this.scale,
nextSize: (canvas) => ({
width: canvas.parentNode.clientWidth * this.scale,
height: canvas.parentNode.clientHeight * this.scale
}),
perAxisStrokeRatios: true
})
}

disconnectedCallback () {
this.teardownCanvasSizing()
}

setupCanvasSizing () {
this.resizeCanvas()

this.onResizeCanvas = () => {
if (this.resizeRaf) {
cancelAnimationFrame(this.resizeRaf)
}

this.resizeRaf = requestAnimationFrame(() => {
this.resizeRaf = requestAnimationFrame(() => {
this.resizeCanvas()
})
})
}

window.addEventListener('resize', this.onResizeCanvas)
screen?.orientation?.addEventListener('change', this.onResizeCanvas)

if (typeof ResizeObserver !== 'undefined' && this.canvas?.parentNode) {
this.resizeObserver = new ResizeObserver(this.onResizeCanvas)
this.resizeObserver.observe(this.canvas.parentNode)
}
}

teardownCanvasSizing () {
if (this.resizeRaf) {
cancelAnimationFrame(this.resizeRaf)
this.resizeRaf = null
}

if (this.onResizeCanvas) {
window.removeEventListener('resize', this.onResizeCanvas)
screen?.orientation?.removeEventListener('change', this.onResizeCanvas)
}

this.resizeObserver?.disconnect()
}

resizeCanvas () {
if (!this.canvas?.parentNode) {
return
}

const width = this.canvas.parentNode.clientWidth
const height = this.canvas.parentNode.clientHeight

if (!width || !height) {
return
}

const nextW = width * this.scale
const nextH = height * this.scale

if (this.canvas.width === nextW && this.canvas.height === nextH) {
return
}

const prevCssW = this.canvas.width / this.scale
const prevCssH = this.canvas.height / this.scale
const ratioX = prevCssW > 0 ? width / prevCssW : 1
const ratioY = prevCssH > 0 ? height / prevCssH : 1

let data = []

if (this.pad) {
data = this.pad.toData()

if (data.length && (ratioX !== 1 || ratioY !== 1)) {
data = data.map((group) => ({
...group,
points: group.points.map((point) => ({
...point,
x: point.x * ratioX,
y: point.y * ratioY
}))
}))
}
}

this.canvas.width = nextW
this.canvas.height = nextH
this.canvas.getContext('2d').scale(this.scale, this.scale)

if (this.pad) {
this.pad.clear()

if (data.length) {
this.pad.fromData(data)
}
}
this.canvasSizing?.teardown()
}

clearSignaturePad () {
Expand Down
106 changes: 12 additions & 94 deletions app/javascript/elements/signature_form.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { target, targetable } from '@github/catalyst/lib/targetable'
import { cropCanvasAndExportToPNG } from '../submission_form/crop_canvas'
import { setupCanvasSizing } from '../submission_form/canvas_sizing'

export default targetable(class extends HTMLElement {
static [target.static] = ['canvas', 'input', 'clear', 'button']
Expand All @@ -25,103 +26,20 @@ export default targetable(class extends HTMLElement {
this.submit()
})

this.setupCanvasSizing()
this.canvasSizing = setupCanvasSizing({
getCanvas: () => this.canvas,
getPad: () => this.pad,
scale: this.scale,
nextSize: (canvas) => ({
width: canvas.parentNode.clientWidth * this.scale,
height: canvas.parentNode.clientHeight * this.scale
}),
perAxisStrokeRatios: true
})
}

disconnectedCallback () {
this.teardownCanvasSizing()
}

setupCanvasSizing () {
this.resizeCanvas()

this.onResizeCanvas = () => {
if (this.resizeRaf) {
cancelAnimationFrame(this.resizeRaf)
}

this.resizeRaf = requestAnimationFrame(() => {
this.resizeRaf = requestAnimationFrame(() => {
this.resizeCanvas()
})
})
}

window.addEventListener('resize', this.onResizeCanvas)
screen?.orientation?.addEventListener('change', this.onResizeCanvas)

if (typeof ResizeObserver !== 'undefined' && this.canvas?.parentNode) {
this.resizeObserver = new ResizeObserver(this.onResizeCanvas)
this.resizeObserver.observe(this.canvas.parentNode)
}
}

teardownCanvasSizing () {
if (this.resizeRaf) {
cancelAnimationFrame(this.resizeRaf)
this.resizeRaf = null
}

if (this.onResizeCanvas) {
window.removeEventListener('resize', this.onResizeCanvas)
screen?.orientation?.removeEventListener('change', this.onResizeCanvas)
}

this.resizeObserver?.disconnect()
}

resizeCanvas () {
if (!this.canvas?.parentNode) {
return
}

const width = this.canvas.parentNode.clientWidth
const height = this.canvas.parentNode.clientHeight

if (!width || !height) {
return
}

const nextW = width * this.scale
const nextH = height * this.scale

if (this.canvas.width === nextW && this.canvas.height === nextH) {
return
}

const prevCssW = this.canvas.width / this.scale
const prevCssH = this.canvas.height / this.scale
const ratioX = prevCssW > 0 ? width / prevCssW : 1
const ratioY = prevCssH > 0 ? height / prevCssH : 1

let data = []

if (this.pad) {
data = this.pad.toData()

if (data.length && (ratioX !== 1 || ratioY !== 1)) {
data = data.map((group) => ({
...group,
points: group.points.map((point) => ({
...point,
x: point.x * ratioX,
y: point.y * ratioY
}))
}))
}
}

this.canvas.width = nextW
this.canvas.height = nextH
this.canvas.getContext('2d').scale(this.scale, this.scale)

if (this.pad) {
this.pad.clear()

if (data.length) {
this.pad.fromData(data)
}
}
this.canvasSizing?.teardown()
}

async submit () {
Expand Down
115 changes: 115 additions & 0 deletions app/javascript/submission_form/canvas_sizing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
function setupCanvasSizing (options) {
let resizeRaf = null
let resizeObserver = null
let intersectionObserver = null

function resizeCanvas () {
const canvas = options.getCanvas()

if (!canvas?.parentNode) {
return
}

const { width: nextW, height: nextH } = options.nextSize(canvas)

if (!nextW || !nextH) {
return
}

if (canvas.width === nextW && canvas.height === nextH) {
return
}

const ratioX = canvas.width > 0 ? nextW / canvas.width : 1
const ratioY = options.perAxisStrokeRatios
? (canvas.height > 0 ? nextH / canvas.height : 1)
: ratioX

let data = []

const pad = options.getPad()

if (pad) {
data = pad.toData()

if (data.length && (ratioX !== 1 || ratioY !== 1)) {
data = data.map((group) => ({
...group,
points: group.points.map((point) => ({
...point,
x: point.x * ratioX,
y: point.y * ratioY
}))
}))
}
}

canvas.width = nextW
canvas.height = nextH
canvas.getContext('2d').scale(options.scale, options.scale)

if (pad) {
pad.clear()

if (data.length) {
pad.fromData(data)
} else {
options.onNoStrokes?.(canvas)
}
}
}

resizeCanvas()

const onResizeCanvas = () => {
if (resizeRaf) {
cancelAnimationFrame(resizeRaf)
}

// Double rAF: orientationchange often fires before layout has settled.
resizeRaf = requestAnimationFrame(() => {
resizeRaf = requestAnimationFrame(() => {
resizeCanvas()
})
})
}

window.addEventListener('resize', onResizeCanvas)
screen?.orientation?.addEventListener('change', onResizeCanvas)

const canvas = options.getCanvas()

if (typeof ResizeObserver !== 'undefined' && canvas?.parentNode) {
resizeObserver = new ResizeObserver(onResizeCanvas)
resizeObserver.observe(canvas.parentNode)
}

if (options.watchVisibility) {
intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
resizeCanvas()
}
})
})

intersectionObserver.observe(canvas)
}

function teardown () {
if (resizeRaf) {
cancelAnimationFrame(resizeRaf)
resizeRaf = null
}

window.removeEventListener('resize', onResizeCanvas)
screen?.orientation?.removeEventListener('change', onResizeCanvas)

resizeObserver?.disconnect()
intersectionObserver?.disconnect()
}

return { resizeCanvas, teardown }
}

export { setupCanvasSizing }
Loading
Loading