Last active
December 16, 2015 21:59
-
-
Save htsign/5503231 to your computer and use it in GitHub Desktop.
現在 version 0.6 ってとこ。
input[type=range]で背景(div[style=background-color:#fff])の透明度を指定できると面白いかも。
This file contains 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
//(function main(){ | |
var mouseStatus = { down: false, grag: false }; | |
var canvasWidth, canvasHeight; | |
var tmpImage, stackImage = [], current = 0; // undo, redo用 | |
var saved = true; // 画像保存フラグ | |
var wrapper = document.createElement("div"); | |
wrapper.id = "canvas-overlay-wrapper"; | |
document.body.insertBefore(wrapper, document.body.firstChild); | |
var canvas = document.createElement("canvas"); | |
canvas.id = "canvas-overlay"; | |
wrapper.appendChild(canvas); | |
canvasWidth = canvas.width; | |
canvasHeight = canvas.height; | |
var backgroundMask = document.createElement("img"); | |
backgroundMask.id = "canvas-overlay-background-mask"; | |
backgroundMask.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAKSURBVAgdY/gPAAEBAQA2X2eAAAAAAElFTkSuQmCC"; | |
backgroundMask.alt = "background"; | |
wrapper.insertBefore(backgroundMask, canvas); | |
var addStyle = function(rules){ | |
var style = document.createElement("style"), i = 0; | |
document.querySelector("head").appendChild(style); | |
rules.forEach(function(val){ | |
style.sheet.insertRule(val, i++); | |
}); | |
}; | |
addStyle([ | |
"#canvas-overlay-wrapper {" | |
+ "position: absolute;" | |
+ "top: 0;" | |
+ "left: 0;" | |
+ "display: block;" | |
+ "z-index: 2147483647;" // z-index の最大値 | |
+"}", | |
"#canvas-overlay-wrapper img {" | |
+ "border: 0;" | |
+"}", | |
"#canvas-overlay {" | |
+ "position: absolute;" | |
+ "cursor: crosshair;" | |
+"}", | |
"#canvas-overlay-background-mask {" | |
+ "position: absolute;" | |
+ "opacity: 0.7;" | |
+"}", | |
// 各ツールのデザインを定義 | |
"#canvas-overlay-wrapper > div[id^=canvas-overlay-] {" | |
+ "position: fixed;" | |
+ "cursor: default;" | |
+ "opacity: 0.5;" | |
+ "transition: opacity 200ms ease-out;" | |
+ "background-color: #666;" | |
+"}", | |
"#canvas-overlay-wrapper > div[id^=canvas-overlay-]:hover {" | |
+ "opacity: 1;" | |
+"}", | |
"#canvas-overlay-toolbox {" | |
+ "top: 32px;" | |
+ "left: 32px;" | |
+ "padding: 1px;" | |
+ "overflow: hidden;" | |
+"}", | |
"#canvas-overlay-menu {" | |
+ "top: 0;" | |
+ "right: 0;" | |
+ "padding: 8px;" | |
+ "color: #eee;" | |
+ "text-shadow: 1px 1px 1.5px #ccc;" | |
+ "border-bottom-left-radius: 8px;" | |
+ "opacity: 0.8 !important;" | |
+"}", | |
"#canvas-overlay-menu .menu-list {" | |
+ "position: absolute;" | |
+ "top: 0;" | |
+ "right: 0;" | |
+ "padding: 2px;" | |
+ "width: 120px;" | |
+ "transform: scaleY(0);" | |
+ "-webkit-transform: scaleY(0);" | |
+ "transition: transform 100ms;" | |
+ "-webkit-transition: -webkit-transform 100ms;" | |
+ "background-color: #000;" | |
+"}", | |
"#canvas-overlay-menu:hover .menu-list, #canvas-overlay-menu .menu-list:hover {" | |
+ "transform: scaleY(1);" | |
+ "-webkit-transform: scaleY(1);" | |
+"}", | |
"#canvas-overlay-menu .menu-item {" | |
+ "margin: 6px;" | |
+ "display: block;" | |
+"}", | |
"#canvas-overlay-menu .menu-item * {" | |
+ "vertical-align: middle;" | |
+"}", | |
"#canvas-overlay-menu img {" | |
+ "width: 16px;" | |
+ "height: 16px;" | |
+"}", | |
"#canvas-overlay-menu span {" | |
+ "margin-left: 4px;" | |
+"}", | |
"#canvas-overlay-palette {" | |
+ "bottom: 32px;" | |
+ "left: -180px;" | |
+ "width: 220px;" | |
+ "height: 200px;" | |
+ "border-radius: 0 6px 6px 0;" | |
+ "transition-property: all !important;" | |
+"}", | |
"#canvas-overlay-palette:hover {" | |
+ "left: 0px;" | |
+"}", | |
"#canvas-overlay-wrapper .item {" | |
+ "position: relative;" | |
+ "margin-left: 2px;" | |
+ "margin-top: 2px;" | |
+ "padding: 0;" | |
+ "display: inline-block;" | |
+ "width: 24px;" | |
+ "height: 24px;" | |
+ "cursor: pointer;" | |
+ "border: 2px outset #99c;" | |
+ "background-color: #669;" | |
+"}", | |
"#canvas-overlay-wrapper .item:nth-child(even) {" | |
+ "margin-right: 2px;" | |
+"}", | |
// mousedown 時にボタンを凹ませる | |
"#canvas-overlay-wrapper .item.dented {" | |
+ "padding: 1px -1px -1px 1px;" | |
+ "border: 2px inset #99c;" | |
+ "background-color: #558;" | |
+"}", | |
// disabled なボタンに半透明な画像をかぶせてボタンを暗くする | |
"#canvas-overlay-wrapper .item > .mask-image {" | |
+ "position: absolute;" | |
+ "top: -2px;" | |
+ "left: -2px;" | |
+ "display: none;" | |
+ "width: 28px;" | |
+ "height: 28px;" | |
+ "cursor: default;" | |
+ "opacity: 0.5;" | |
+"}", | |
"#canvas-overlay-wrapper .item[disabled] > .mask-image {" | |
+ "display: inline-block;" | |
+"}", | |
// 保存用画像のスタイル定義 | |
"#saved-image {" | |
+ "position: fixed;" | |
+ "line-height: 0;" | |
+ "overflow: hidden;" | |
+ "border: 3px ridge #666;" | |
+"}", | |
"#saved-image > img {" | |
+ "max-width: 320px;" | |
+ "max-height: 320px;" | |
+ "cursor: pointer;" | |
+"}" | |
]); | |
// Canvas の初期設定 | |
var ctx = canvas.getContext("2d"); | |
ctx.lineCap = "round"; | |
ctx.globalCompositeOperation = "lighter"; | |
var sizing = function(){ | |
var image = ctx.getImageData(0, 0, canvasWidth, canvasHeight); | |
canvas.width = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth); | |
canvas.height = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight); | |
canvasWidth = canvas.width; | |
canvasHeight = canvas.height; | |
backgroundMask.width = canvasWidth; | |
backgroundMask.height = canvasHeight; | |
ctx.putImageData(image, 0, 0); | |
}; | |
sizing(); | |
window.addEventListener("resize", sizing, true); // ウィンドウリサイズ時に canvas の大きさを調整 | |
// toolbox, menu 用のイベント定義 | |
var buttonMouseDownEvent = function(evt){ | |
evt.target.className += " dented"; | |
}; | |
var buttonMouseUpEvent = function(evt){ | |
evt.target.className = "item"; | |
}; | |
// toolbox の定義、作成 | |
var toolbox = { | |
init: function(){ | |
document.getElementById("canvas-overlay-toolbox-undo").disabled = true; | |
document.getElementById("canvas-overlay-toolbox-redo").disabled = true; | |
}, | |
item: { | |
undo: { | |
image: { | |
src: "", | |
text: "元に戻す" | |
}, | |
func: { | |
click: function(evt){ | |
ctx.putImageData(stackImage[--current], 0, 0); | |
document.getElementById("canvas-overlay-toolbox-redo").disabled = false; | |
if (current <= 0) { | |
evt.target.disabled = true; | |
} | |
} | |
} | |
}, | |
redo: { | |
image: { | |
src: "", | |
text: "やり直す" | |
}, | |
func: { | |
click: function(evt){ | |
ctx.putImageData(stackImage[++current], 0, 0); | |
document.getElementById("canvas-overlay-toolbox-undo").disabled = false; | |
if (current >= stackImage.length) { | |
evt.target.disabled = true; | |
} | |
} | |
} | |
} | |
} | |
}; | |
var createToolbar = function(){ | |
var tb = document.createElement("div"); | |
tb.id = "canvas-overlay-toolbox"; | |
wrapper.appendChild(tb); | |
var i, f; | |
for (i in toolbox.item) { | |
var button = document.createElement("button"); | |
button.id = "canvas-overlay-toolbox-" + i; | |
button.className = "item"; | |
button.title = toolbox.item[i].image.text; | |
button.style.backgruondImage = "data:image/png;base64," + toolbox.item[i].image.src; | |
for (f in toolbox.item[i].func) { | |
button.addEventListener(f, toolbox.item[i].func[f], false); | |
} | |
var image = document.createElement("img"); | |
image.className = "mask-image"; | |
image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAKSURBVAgdY2AAAAACAAHPyDXlAAAAAElFTkSuQmCC"; | |
image.alt = i + "(disabled)"; | |
button.appendChild(image); | |
button.addEventListener("mousedown", buttonMouseDownEvent, false); | |
button.addEventListener("mouseup", buttonMouseUpEvent, false); | |
button.addEventListener("mouseout", buttonMouseUpEvent, false); | |
tb.appendChild(button); | |
} | |
toolbox.init(); | |
}; | |
window.setTimeout(createToolbar, 0); | |
// menu の定義、作成 | |
var menu = { | |
init: function(){ | |
// なんか書いておきたいと思う | |
}, | |
item: { | |
saveToPhoto: { | |
image: { | |
src: "", | |
text: "保存する" | |
}, | |
func: { | |
click: function(evt){ | |
var savedImage = document.createElement("img"); | |
savedImage.src = canvas.toDataURL(); | |
savedImage.alt = "保存済み画像"; | |
savedImage.title = "右クリックで保存してください。\nクリックで閉じます。"; | |
savedImage.style.visibility = "hidden"; | |
// どっかに枠作ってぽーんっと飛んでいく感じのアニメーションで放り込みたい。 | |
var frame = document.createElement("div"); | |
frame.id = "saved-image"; | |
frame.appendChild(savedImage); | |
wrapper.insertBefore(frame, backgroundMask); | |
var imgWidth = parseFloat(window.getComputedStyle(savedImage).width); | |
var imgHeight = parseFloat(window.getComputedStyle(savedImage).height); | |
if (imgWidth >= imgHeight) { | |
savedImage.width = 320; | |
savedImage.removeAttribute("height"); | |
} else { | |
savedImage.height = 320; | |
savedImage.removeAttribute("width"); | |
} | |
frame.style.left = (window.innerWidth - parseFloat(window.getComputedStyle(frame).width)) / 2 + "px"; | |
frame.style.top = (window.innerHeight - parseFloat(window.getComputedStyle(frame).height)) / 2 + "px"; | |
var duration = 1000, fps = 60, f = 1, interval = duration/fps; | |
var diff = { | |
x: canvasWidth - imgWidth, | |
y: canvasHeight - imgHeight | |
}; | |
var animate = function(){ | |
var height = window.innerHeight / 2; | |
height *= Math.sin(f * Math.PI / fps); | |
console.log((f * diff.x / 2 - canvasWidth / 2) / fps + document.documentElement.scrollLeft+"\n", | |
(f * diff.y / 2 - canvasHeight / 2) / fps + document.documentElement.scrollTop - height+"\n", | |
diff.x - diff.x * (f/fps) + imgWidth+"\n", | |
diff.y - diff.y * (f/fps) + imgHeight+"\n"); | |
ctx.clearRect(0, 0, canvasWidth, canvasHeight); | |
ctx.drawImage(savedImage, | |
(f * diff.x - canvasWidth / 2) / fps + document.documentElement.scrollLeft, | |
(f * diff.y - canvasHeight / 2) / fps + document.documentElement.scrollTop - height, | |
diff.x - diff.x * (f/fps) + imgWidth, | |
diff.y - diff.y * (f/fps) + imgHeight); | |
if (f++ <= fps) { | |
window.setTimeout(animate, interval); | |
} | |
else { | |
savedImage.style.visibility = "visible"; | |
savedImage.addEventListener("click", function(){ | |
wrapper.removeChild(frame); | |
}, false); | |
wrapper.appendChild(frame); // fが終端に達したらframeをcanvasより手前に持ってくる | |
ctx.clearRect(0, 0, canvasWidth, canvasHeight); | |
saved = true; | |
window.alert("右クリックで保存してください。\nクリックで閉じます。"); | |
} | |
}; | |
window.setTimeout(animate, 0); | |
} | |
} | |
}, | |
close: { | |
image: { | |
src: "", | |
text: "終了" | |
}, | |
func: { | |
click: function(evt){ | |
if (!saved || stackImage.length > 1) { | |
if (window.confirm("終了すると作業途中のイラストは破棄されます。\nよろしいですか?")) { | |
exit(); | |
} | |
} | |
else { | |
exit(); | |
} | |
function exit() { | |
document.body.removeChild(wrapper); | |
} | |
} | |
} | |
} | |
} | |
}; | |
var createMenu = function(){ | |
var m = document.createElement("div"); | |
m.id = "canvas-overlay-menu"; | |
m.appendChild(document.createTextNode("canvas-overlay メニュー")); | |
wrapper.appendChild(m); | |
var list = document.createElement("div"); | |
list.className = "menu-list"; | |
m.appendChild(list); | |
var i, f; | |
for (i in menu.item) { | |
var a = document.createElement("a"); | |
a.id = "canvas-overlay-menu-" + i; | |
a.className = "menu-item"; | |
a.href = "#"; | |
for (f in menu.item[i].func) { | |
a.addEventListener(f, menu.item[i].func[f], false); | |
} | |
var image = document.createElement("img"); | |
image.src = "data:image/png;base64," + menu.item[i].image.src; | |
image.alt = i; | |
image.title = menu.item[i].image.text; | |
a.appendChild(image); | |
var span = document.createElement("span"); | |
span.appendChild(document.createTextNode(image.title)); | |
a.appendChild(span); | |
a.onclick = function(){ return false }; | |
list.appendChild(a); | |
} | |
menu.init(); | |
}; | |
window.setTimeout(createMenu, 0); | |
// palette の定義、作成 | |
var palette = { | |
init: function(){ | |
// なんか書いておきたいと思う | |
}, | |
item: { | |
// 必要か分からんがとりあえず残しとく | |
} | |
}; | |
var createPalette = function(){ | |
var plt = document.createElement("div"); | |
plt.id = "canvas-overlay-palette"; | |
wrapper.appendChild(plt); | |
plt.appendChild(document.createTextNode("この辺にパレット作る予定")); | |
palette.init(); | |
}; | |
window.setTimeout(createPalette, 0); | |
// canvas 用のイベント定義 | |
var startEvent = function(evt){ | |
mouseStatus.down = true; | |
tmpImage = ctx.getImageData(0, 0, canvasWidth, canvasHeight); | |
ctx.beginPath(); | |
ctx.moveTo(evt.pageX, evt.pageY); | |
}; | |
var moveEvent = function(evt){ | |
if (mouseStatus.down) { | |
mouseStatus.drag = true; | |
ctx.lineTo(evt.pageX, evt.pageY); | |
ctx.stroke(); | |
} | |
}; | |
var endEvent = function(evt){ | |
if (mouseStatus.drag) { | |
stackImage.splice(current++, stackImage.length); | |
stackImage[stackImage.length] = tmpImage; | |
document.getElementById("canvas-overlay-toolbox-undo").disabled = false; | |
document.getElementById("canvas-overlay-toolbox-redo").disabled = true; | |
saved = false; | |
} | |
mouseStatus.down = false; | |
mouseStatus.drag = false; | |
ctx.closePath(); | |
}; | |
canvas.addEventListener("mousedown", startEvent, false); | |
canvas.addEventListener("mousemove", moveEvent, false); | |
canvas.addEventListener("mouseup", endEvent, false); | |
canvas.addEventListener("mouseout", endEvent, false); | |
//})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment