<html lang="en">
<head>
<meta charset="UTF-8">
<title>Paint</title>
<style>
#content { position: relative; }
#cvs { border: 1px solid #c00; }
#cvsTmp { position: absolute; top: 1px; left: 1px; }
</style>
</head>
<body>
<p>
<label>
Object type:
<select id="selectTool">
<option value="line">Linje</option>
<option value="pencil">Blyant</option>
<option value="rect">Rektangel</option>
<option value="circle">Sirkel</option>
<option value="oval">Oval</option>
<option value="polygon">Polygon</option>
</select>
</label>
<label>
History:
<select id="historySelect">
</select>
</label>
<label>
<input type="button" id="undoAction" value="Undo">
</label>
</p>
<div id="content">
<canvas id="cvs" width="1024" height="512"></canvas>
</div>
<script>
if (window.addEventListener) {
window.addEventListener('load', function () {
var canvas;
var context;
var canvasTmp;
var contextTmp;
var tool;
var toolDefault = 'line';
var history = [];
var historySelect;
////////// CANVAS //////////
function init() {
canvasTmp = document.getElementById('cvs');
if (!canvasTmp) {
return;
}
if (!canvasTmp.getContext) {
return;
}
historySelect = document.getElementById('historySelect')
historySelect.addEventListener('change', () => {
restoreHistoryAction(historySelect.value)
})
contextTmp = canvasTmp.getContext('2d');
if (!contextTmp) {
return;
}
var content = canvasTmp.parentNode;
canvas = document.createElement('canvas');
if (!canvas) {
return;
}
canvas.id = 'cvsTmp';
canvas.width = canvasTmp.width;
canvas.height = canvasTmp.height;
content.appendChild(canvas);
context = canvas.getContext('2d');
////////// TOOLS AND MOUSE //////////
var toolSelect = document.getElementById('selectTool');
if (!toolSelect) {
return;
}
toolSelect.addEventListener('change', ev_tool_change, false);
if (tools[toolDefault]) {
tool = new tools[toolDefault]();
toolSelect.value = toolDefault;
}
canvas.addEventListener('mousedown', evMouse, false);
canvas.addEventListener('mousemove', evMouse, false);
canvas.addEventListener('mouseup', evMouse, false);
drawCanvas()
}
function evMouse(ev) {
if (ev.layerX || ev.layerX == 0) {
ev._x = ev.layerX;
ev._y = ev.layerY;
}
var evHandler = tool[ev.type];
if (evHandler) {
evHandler(ev);
}
}
////////// EXCACT POSITION FOR MOUSE ON CANVAS //////////
function mouseAction(ev) {
if (ev.layerX || ev.layerX == 0) {
ev._x = ev.layerX;
ev._y = ev.layerY;
}
var toolEventHandler = tool[ev.type];
if (toolEventHandler) {
toolEventHandler(ev);
}
}
function ev_tool_change(ev) {
if (tools[this.value]) {
tool = new tools[this.value]();
}
}
////////// UPDATE CANVAS ON INTERVAL TIMEOUT //////////
function drawCanvas() {
contextTmp.drawImage(canvas, 0, 0);
history.push(contextTmp.getImageData(0, 0, canvasTmp.width, canvasTmp.height))
updateHistorySelection()
context.clearRect(0, 0, canvas.width, canvas.height);
}
/////////THE TOOLS//////////
var tools = {};
//// PENCIL ////
tools.pencil = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
context.beginPath();
context.moveTo(ev._x, ev._y);
tool.started = true;
};
this.mousemove = function (ev) {
if (tool.started) {
context.lineTo(ev._x, ev._y);
context.stroke();
}
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// RECTANGLE ////
tools.rect = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
var x = Math.min(ev._x, tool.x0),
y = Math.min(ev._y, tool.y0),
w = Math.abs(ev._x - tool.x0),
h = Math.abs(ev._y - tool.y0);
context.clearRect(0, 0, canvas.width, canvas.height);
if (!w || !h) {
return;
}
context.fillStyle = 'hsl(' + 360 * Math.random() + ', 85%, 50%)';
context.fillRect(x, y, w, h);
context.strokeRect(x, y, w, h);
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// LINE ////
tools.line = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.moveTo(tool.x0, tool.y0);
context.lineTo(ev._x, ev._y);
context.stroke();
context.closePath();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// CIRCLE ////
tools.circle = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
var radius = Math.max(Math.abs(ev._x - tool.x0), Math.abs(ev._y - tool.y0)) / 2;
var x = Math.min(ev._x, tool.x0) + radius;
var y = Math.min(ev._y, tool.y0) + radius;
context.fillStyle = 'hsl(' + 360 * Math.random() + ', 85%, 50%)';
context.beginPath();
context.arc(x, y, radius, 0, Math.PI * 2, false);
context.stroke();
context.closePath();
context.fill();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// OVAL ////
tools.oval = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
var radius1 = Math.abs(ev._x - tool.x0);
var radius2 = Math.abs(ev._y - tool.y0);
var scaleX = radius1 / (Math.max(radius1, radius2));
var x = tool.x0 / scaleX;
var scaleY = radius2 / (Math.max(radius1, radius2));
var y = tool.y0 / scaleY;
context.fillStyle = 'hsl(' + 360 * Math.random() + ', 100%, 50%)';
context.save();
context.scale(scaleX, scaleY);
context.beginPath();
context.arc(x, y, Math.max(radius1, radius2), 0, 2 * Math.PI);
context.restore();
context.stroke();
context.closePath();
context.fill();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// POLYGON ////
tools.polygon = function () {
var tool = this;
this.started = false;
this.mousedown = function (ev) {
tool.started = true;
tool.x0 = ev._x;
tool.y0 = ev._y;
};
this.mousemove = function (ev) {
if (!tool.started) {
return;
}
context.clearRect(0, 0, canvas.width, canvas.height);
var numberOfSides = 6,
size = Math.max(Math.abs(tool.x0 - ev._x), Math.abs(tool.y0 - ev._y)),
Xcenter = tool.x0,
Ycenter = tool.y0;
context.fillStyle = 'hsl(' + 360 * Math.random() + ', 100%, 50%)';
context.save();
context.beginPath();
context.moveTo(Xcenter + size * Math.cos(0), Ycenter + size * Math.sin(0));
for (var i = 1; i <= numberOfSides; i += 1) {
context.lineTo(Xcenter + size * Math.cos(i * 2 * Math.PI / numberOfSides), Ycenter + size * Math.sin(i * 2 * Math.PI / numberOfSides));
}
context.restore();
context.stroke();
context.fill();
};
this.mouseup = function (ev) {
if (tool.started) {
tool.mousemove(ev);
tool.started = false;
drawCanvas();
}
};
};
//// UNDO ////
function undoActionClick() {
if (history.length <= 1) return
history.pop()
contextTmp.putImageData(history[history.length - 1], 0, 0)
updateHistorySelection();
}
function updateHistorySelection() {
historySelect.innerHTML = ''
history.forEach((entry, index) => {
let option = document.createElement('option')
option.value = index
option.textContent = index === 0 ? 'Start ' : 'Action ' + index
historySelect.appendChild(option)
})
historySelect.selectedIndex = history.length - 1
}
function restoreHistoryAction(index) {
contextTmp.putImageData(history[index], 0, 0)
}
undoAction = document.getElementById("undoAction");
undoAction.addEventListener("click", undoActionClick, false);
init();
}, false);
}
</script>
</body>
</html>
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |