Last active
January 9, 2024 15:16
-
-
Save tomhodgins/bffcc0dce83f2d593afc to your computer and use it in GitHub Desktop.
Draw <svg> inside the browser! On mousedown and touchstart it begins drawing a line, drops nodes as you mousemove or touchmove, and then finishes the line where you mouseup or touchend. It also has support for line width and color, exporting the current drawing by email as well as downloading the drawing as an svg file.You can also import SVGs. h…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html manifest=sketch.manifest> | |
<head> | |
<meta charset=utf-8> | |
<title>Sketch</title> | |
<meta name=apple-mobile-web-app-capable content=yes> | |
<meta name=apple-mobile-web-app-status-bar-style content=black> | |
<meta name=viewport content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, minimal-ui"> | |
<link href="http://staticresource.com/formal.css" rel=stylesheet type=text/css> | |
<script>eval(unescape(escape('♶♡♲☠♩☽♤♯♣♵♭♥♮♴☮♣♲♥♡♴♥♅♬♥♭♥♮♴☨☧♣♡♮♶♡♳☧☩☬♣☽♩☮♧♥♴♃♯♮♴♥♸♴☨☧☲♤☧☩☬♦☽♤♯♣♵♭♥♮♴☮♣♲♥♡♴♥♅♬♥♭♥♮♴☨☧♬♩♮♫☧☩☬♭☽♤♯♣♵♭♥♮♴☮♣♲♥♡♴♥♅♬♥♭♥♮♴☨☧♬♩♮♫☧☩☻♩☮♷♩♤♴♨☽☲☰☰☻♩☮♨♥♩♧♨♴☽☲☰☰☻♣☮♦♩♬♬♓♴♹♬♥☽☧♬♩♧♨♴♧♯♬♤♥♮♲♯♤♹♥♬♬♯♷☧☻♣☮♢♥♧♩♮♐♡♴♨☨☩☻♣☮♭♯♶♥♔♯☨☱☰☬☰☩☻♣☮♬♩♮♥♔♯☨☱☹☰☬☰☩☻♣☮♱♵♡♤♲♡♴♩♣♃♵♲♶♥♔♯☨☲☰☰☬☰☬☲☰☰☬☱☰☩☻♣☮♬♩♮♥♔♯☨☲☰☰☬☱☹☰☩☻♣☮♱♵♡♤♲♡♴♩♣♃♵♲♶♥♔♯☨☲☰☰☬☲☰☰☬☱☹☰☬☲☰☰☩☻♣☮♬♩♮♥♔♯☨☱☰☬☲☰☰☩☻♣☮♱♵♡♤♲♡♴♩♣♃♵♲♶♥♔♯☨☰☬☲☰☰☬☰☬☱☹☰☩☻♣☮♬♩♮♥♔♯☨☰☬☱☰☩☻♣☮♱♵♡♤♲♡♴♩♣♃♵♲♶♥♔♯☨☰☬☰☬☱☰☬☰☩☻♣☮♣♬♯♳♥♐♡♴♨☨☩☻♣☮♦♩♬♬☨☩☻♣☮♳♴♲♯♫♥♓♴♹♬♥☽☧♲♥♤☧☻♣☮♬♩♮♥♗♩♤♴♨☽☳☰☻♣☮♬♩♮♥♃♡♰☽☧♲♯♵♮♤☧☻♣☮♢♥♧♩♮♐♡♴♨☨☩☻♣☮♭♯♶♥♔♯☨☴☵☬☱☳☰☩☻♣☮♢♥♺♩♥♲♃♵♲♶♥♔♯☨☴☵☬☱☳☰☬☷☰☬☲☰☰☬☱☰☵☬☱☳☰☩☻♣☮♢♥♺♩♥♲♃♵♲♶♥♔♯☨☱☰☵☬☱☳☰☬☱☴☰☬☵☰☬☱☶☵☬☱☳☰☩☻♣☮♳♴♲♯♫♥☨☩☻♣☮♣♬♯♳♥♐♡♴♨☨☩☻♣☮♳♴♲♯♫♥♓♴♹♬♥☽☧♢♬♡♣♫☧☻♣☮♬♩♮♥♃♡♰☽☧♢♵♴♴☧☻♣☮♢♥♧♩♮♐♡♴♨☨☩☻♣☮♭♯♶♥♔♯☨☱☴☰☬☲☵☩☻♣☮♬♩♮♥♔♯☨☴☰☬☱☲☵☩☻♣☮♳♴♲♯♫♥☨☩☻♣☮♦♩♬♬♓♴♹♬♥☽☧♢♬♡♣♫☧☻♣☮♢♥♧♩♮♐♡♴♨☨☩☻♣☮♭♯♶♥♔♯☨☳☰☬☱☱☴☩☻♣☮♬♩♮♥♔♯☨☳☰☬☱☴☰☩☻♣☮♬♩♮♥♔♯☨☵☱☬☱☳☵☩☻♣☮♣♬♯♳♥♐♡♴♨☨☩☻♣☮♦♩♬♬☨☩☻♦☮♴♹♰♥☽☧♩♭♡♧♥☯♸☭♩♣♯♮☧☻♦☮♲♥♬☽☧♳♨♯♲♴♣♵♴☠♩♣♯♮☧☻♭☮♲♥♬☽☧♡♰♰♬♥☭♴♯♵♣♨☭♩♣♯♮☧☻♦☮♨♲♥♦☽♭☮♨♲♥♦☽♩☮♴♯♄♡♴♡♕♒♌☨☧♩♭♡♧♥☯♰♮♧☧☩☻♤♯♣♵♭♥♮♴☮♧♥♴♅♬♥♭♥♮♴♳♂♹♔♡♧♎♡♭♥☨☧♨♥♡♤☧☩♛☰♝☮♡♰♰♥♮♤♃♨♩♬♤☨♦☩☻♤♯♣♵♭♥♮♴☮♧♥♴♅♬♥♭♥♮♴♳♂♹♔♡♧♎♡♭♥☨☧♨♥♡♤☧☩♛☰♝☮♡♰♰♥♮♤♃♨♩♬♤☨♭☩☻').replace(/u../g,'')))</script> | |
<style> | |
html, body, svg { | |
margin: 0; | |
width: 100%; | |
height: 100%; | |
background: #222; | |
overflow: hidden; | |
cursor: none; | |
position: fixed; | |
} | |
svg { | |
position: relative; | |
background: white; | |
} | |
nav { | |
display: block; | |
position: fixed; | |
bottom: 5px; | |
left: 5px; | |
} | |
#cursor, div { | |
display: block; | |
width: 5px; | |
height: 5px; | |
border-radius: 100%; | |
background: black; | |
position: absolute; | |
opacity: .5; | |
pointer-events: none; | |
transition: opacity .1s ease-in-out; | |
} | |
</style> | |
</head> | |
<svg xmlns=http://www.w3.org/2000/svg version=1.1></svg> | |
<nav> | |
<select onchange="document.getElementById('color').value=cursor.style.background=brush=this.value"> | |
<option value=#000000 selected>Black</option> | |
<option value=#0000ff>Blue</option> | |
<option value=#008000>Green</option> | |
<option value=#ff0000>Red</option> | |
<option value=#ffd700>Yellow</option> | |
<option value=#ffffff>White</option> | |
</select> | |
<input id=color type=color value=#000000 style="width:75px;height:32px;" onchange="cursor.style.background=brush=this.value;"> | |
<input id=width type=number min=1 value=5 style="width:55px" oninput="radius=this.value/2;cursor.style.width=this.value+'px';cursor.style.height=this.value+'px'"> | |
<input type=button onclick=undo() value=undo> | |
<input type=button onclick=clean() value=clear> | |
<input type=button onclick=render() value=export> | |
<input type=button onclick=download() value=download> | |
<input id=upload type=file accept=image/svg+xml style="width:110px"> | |
</nav> | |
<div id=cursor></div> | |
<script> | |
var board = document.getElementsByTagName('svg')[0], | |
cursor = document.getElementById('cursor'), | |
gesture = false, | |
line = '', | |
brush = 'black', | |
radius = 2.5; | |
if (localStorage.svg){ | |
document.body.removeChild(board) | |
document.getElementsByTagName('nav')[0].insertAdjacentHTML('beforebegin', localStorage.svg) | |
board = document.getElementsByTagName('svg')[0] | |
} | |
// Start | |
board.addEventListener('mousedown',lineStart) | |
board.addEventListener('touchstart',lineStart) | |
function lineStart(e){ | |
line = 'M'+(e.clientX||e.touches[0].clientX)+','+(e.clientY||e.touches[0].clientY)+' ' | |
cursor.style.opacity = 1 | |
gesture = true | |
e.preventDefault() | |
} | |
// Move | |
board.addEventListener('mousemove',lineMove) | |
board.addEventListener('touchmove',lineMove) | |
function lineMove(e){ | |
if (gesture == true){ | |
line += 'L'+(e.clientX||e.touches[0].clientX)+','+(e.clientY||e.touches[0].clientY)+' ' | |
trace((e.clientX||e.touches[0].clientX),(e.clientY||e.touches[0].clientY)) | |
} | |
cursor.style.top = e.clientY-radius+'px' | |
cursor.style.left = e.clientX-radius+'px' | |
} | |
// End | |
board.addEventListener('mouseup',lineEnd) | |
board.addEventListener('touchend',lineEnd) | |
function lineEnd(e){ | |
line += 'L'+(e.clientX||e.changedTouches[0].clientX)+','+(e.clientY||e.changedTouches[0].clientY) | |
cursor.style.opacity = .5 | |
var path = document.createElementNS('http://www.w3.org/2000/svg','path') | |
path.setAttributeNS(null,'d',line) | |
path.setAttributeNS(null,'fill','none') | |
path.setAttributeNS(null,'stroke-linecap','round') | |
path.setAttributeNS(null,'stroke',document.getElementById('color').value) | |
path.setAttributeNS(null,'stroke-width',document.getElementById('width').value) | |
board.appendChild(path) | |
board.innerHTML = board.innerHTML // force SVG repaint after DOM change | |
gesture = false | |
localStorage.svg = new XMLSerializer().serializeToString(board) | |
} | |
function undo(){ | |
var paths = document.querySelectorAll('#board path'); | |
board.removeChild(paths[paths.length-1]) | |
} | |
function clean(){ | |
if(window.confirm('Would you like to clear the sketch?','')===true){ | |
var paths = board.querySelectorAll('path').length | |
for (i=0;i<paths;i++){ | |
board.removeChild(board.querySelectorAll('path')[0]) | |
} | |
localStorage.svg = '' | |
} | |
} | |
function render(){ | |
var link = document.createElement('a'), | |
time = Date.now(); | |
link.href = 'mailto:?subject=exported%20sketch&body=%3C%3Fxml%20version%3D%221.0%22%20standalone%3D%22no%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E'+encodeURIComponent(new XMLSerializer().serializeToString(board)) | |
link.id = time | |
document.body.appendChild(link) | |
document.getElementById(time).dispatchEvent(new MouseEvent('click')) | |
document.getElementById(time).parentNode.removeChild(document.getElementById(time)) | |
} | |
function download(){ | |
var link = document.createElement('a'), | |
time = Date.now(), | |
svg = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'+new XMLSerializer().serializeToString(board); | |
link.id = time | |
link.setAttribute('download','sketch.svg') | |
link.href = 'data:text/html;charset=utf-8,' + encodeURIComponent(svg) | |
document.body.appendChild(link) | |
document.getElementById(time).dispatchEvent(new MouseEvent('click')) | |
document.getElementById(time).parentNode.removeChild(document.getElementById(time)) | |
} | |
function trace(x,y){ | |
var dot = document.createElement('div'); | |
dot.style.top = y-radius+'px' | |
dot.style.left = x-radius+'px' | |
dot.style.background = brush | |
dot.style.width = dot.style.height = radius*2+'px' | |
document.body.appendChild(dot) | |
setTimeout(function(){dot.style.opacity=0},500) | |
setTimeout(function(){document.body.removeChild(dot)},1000) | |
} | |
document.getElementById('upload').addEventListener('change',importSVG) | |
function importSVG(e){ | |
var file = e.target.files[0]; | |
if (file){ | |
var reader = new FileReader() | |
reader.onload = function(e){ | |
var content = e.target.result | |
board.innerHTML = content | |
} | |
} | |
reader.readAsText(file) | |
} | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CACHE MANIFEST | |
# version 2 | |
CACHE: | |
sketch.html | |
formal.css | |
NETWORK: | |
* |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment