Skip to content

Instantly share code, notes, and snippets.

@htsign
Last active December 16, 2015 21:59
Show Gist options
  • Save htsign/5503231 to your computer and use it in GitHub Desktop.
Save htsign/5503231 to your computer and use it in GitHub Desktop.
現在 version 0.6 ってとこ。 input[type=range]で背景(div[style=background-color:#fff])の透明度を指定できると面白いかも。
//(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