如何使用 Javascript 在图像上绘制并将追踪捕捉到网格?
How to draw on an image using Javascript and snap tracing to a grid?
我想实现一个前端工具,允许用户:
- 在图像上描绘手绘区域
- 跟踪必须关闭
- 跟踪应对齐到网格(例如 32 x 32 像素)
我发现这个 JS 库 (http://ianli.com/sketchpad/) 看起来非常好并且符合要求。
我想没有现成的库专门满足我的需要,所以请随时就如何解决这个问题提出任何建议。
最后的 objective 是输出一个 JSON(或其他坐标列表)映射到图像上的一个 ara,但具有 32 x 32 像素 "resolution"。
跟踪的最终结果应如下所示:
我希望你会发现这个脚本有用。在 JSFiddle 上试用。按 Enter
键选择选项。
window.addEventListener('load', function(){
var O = {
doc: document,
body: document.body,
wd: window,
ce: function(a, b){
return a.appendChild(a = O.doc.createElement(b)), a;
},
cc: function(){
var c, g, gn;
c = O.ce(O.body, 'canvas');
c.width = O.w;
c.height = O.h;
g = c.getContext('2d');
gn = {
sfc: function(a){ // Set fill color
g.fillStyle = a;
},
slc: function(a){ // Set line color
g.strokeStyle = a;
},
slw: function(a){ // Set line width
g.lineWidth = a;
},
bp: function(){ // Begin path
g.beginPath();
},
mt: function(a, b){ // Move to
g.moveTo(gn.tp(0, a), gn.tp(1, b));
},
t: { // Transform
x: 0,
y: 0,
sx: 1,
sy: 1
},
tp: function(a, b){ // Tranform point
return O.mfl(a ? b * gn.t.sy + gn.t.y : b * gn.t.sx + gn.t.x) + .5;
},
tps: function(a, b){ // Tranform distance between points
return O.mfl(a ? b * gn.t.sy : b * gn.t.sx);
},
lt: function(a, b){ // Line to
g.lineTo(gn.tp(0, a), gn.tp(1, b));
},
cp: function(){ // Close path
g.closePath();
},
fill: function(){
g.fill();
},
line: function(){
g.stroke();
},
arc: function(a, b, c, d, e, f){
g.arc(gn.tp(0, a), gn.tp(1, b), c, d, e, f);
},
rc: function(a, b, c, d){ // Rectangle
g.rect(gn.tp(0, a) - .5, gn.tp(1, b) - .5, gn.tps(0, c), gn.tps(1, d));
},
frc: function(a, b, c, d){ // Fill rectangle
g.fillRect(gn.tp(0, a) - .5, gn.tp(1, b) - .5, gn.tps(0, c), gn.tps(1, d));
},
st: function(a, b, c, d){
gn.t.x = a;
gn.t.y = b;
gn.t.sx = c;
gn.t.sy = d;
gn.slw(1 / O.min(c, d));
},
cls: function(){
gn.sfc(O.cols.white);
gn.slc(O.cols.black);
gn.frc(0, 0, O.w, O.h);
}
};
gn.cls();
return gn;
},
init: function(){
O.body.style.margin = '0px';
O.body.style.padding = '0px';
O.cols = {
red: O.rgb(1, 0, 0),
green: O.rgb(0, 1, 0),
blue: O.rgb(0, 0, 1),
white: O.rgb(1, 1, 1),
black: O.rgb(0, 0, 0),
yellow: O.rgb(1, 1, 0),
lightBlue: O.rgb(0, 1, 1),
purple: O.rgb(1, 0, 1),
gray: O.rgb(.5, .5, .5)
};
O.w = O.wd.innerWidth;
O.h = O.wd.innerHeight;
O.wh = O.w / 2;
O.hh = O.h / 2;
O.pih = O.pi / 2;
O.pi2 = O.pi * 2;
main(O);
},
rf: function(b, c){
var a = new XMLHttpRequest();
a.open('get', b, true);
a.onreadystatechange = function(){
a.readyState != 4 || a.status && a.status != 200 || c(a.responseText);
};
a.send();
},
ca: function(a, b){
return (Array(a) + '').split(',').map(b);
},
pad: function(a, b, c){
return (a += '').length >= b ? a : Array(b - a.length + 1).join(c) + a;
},
rgb: function(a, b, c){
return '#' + O.rgbf(a) + O.rgbf(b) + O.rgbf(c);
},
rgbf: function(a){
return O.pad(O.mro(a * 255).toString(16), 2, '0');
},
cols: null,
ael: function(a, b){
O.wd.addEventListener(a, b);
},
rel: function(a, b){
O.wd.removeEventListener(a, b);
},
w: null,
h: null,
raf: function(a){ // Request animation frame
O.wd.requestAnimationFrame(a);
},
rand: function(a){ // Random integer
return O.mfl(O.mra * a);
},
randf: function(a){ // Random float
return O.mra() * a;
},
rq: function(a, b){ // Require
O.rf('/' + a + '/1.js', function(a){
eval(a);
b();
});
},
wh: null,
hh: null,
pi: Math.PI,
pih: null,
pi2: null,
mfl: function(a){ // Math floor
return Math.floor(a);
},
mra: function(){ // Math random
return Math.random();
},
mro: function(a){ // Math round
return Math.round(a);
},
mce: function(a){ // Math ceil
return Math.ceil(a);
},
max: function(a, b){
return Math.max(a, b);
},
min: function(a, b){
return Math.min(a, b);
}
};
O.init();
});
function main(O){
var gridX = 32;
var gridY = 32;
var coords, pressed, cx, cy, g;
(function(){
g = O.cc();
coords = [];
pressed = 0;
O.ael('keydown', onKeyDown);
O.ael('mousedown', onMouseDown);
O.ael('mousemove', onMouseMove);
O.ael('mouseup', onMouseUp);
})();
function onKeyDown(a){
switch(a.keyCode){
case 13:
switch(+prompt('0 - Clear\n1 - Get JSON\n2 - Paste JSON')){
case 0:
coords.length = 0;
g.cls();
break;
case 1:
prompt('', JSON.stringify(coords));
break;
case 2:
coords = JSON.parse(prompt('Pase JSON:'));
drawCoords();
break;
}
break;
}
}
function onMouseDown(a){
if(!a.button){
cx = snapToGrid(0, a.clientX);
cy = snapToGrid(1, a.clientY);
pressed = 1;
}
}
function onMouseMove(a){
var x, y;
if(pressed){
x = snapToGrid(0, a.clientX);
y = snapToGrid(1, a.clientY);
if(x != cx){
g.bp();
g.mt(cx, cy);
g.lt(x, cy);
g.line();
cx = x;
coords.push(cx, cy);
}
if(y != cy){
g.bp();
g.mt(cx, cy);
g.lt(cx, y);
g.line();
cy = y;
coords.push(cx, cy);
}
}
}
function onMouseUp(a){
if(!a.button){
pressed = 0;
coords.push(null);
}
}
function snapToGrid(a, b){
return a ? O.mro(b / gridY) * gridY : O.mro(b / gridX) * gridX;
}
function drawCoords(){
var a = coords.slice(), q;
g.cls();
g.bp();
g.mt(a.shift(), a.shift());
while(a.length){
if((q = a.shift()) == null){
g.mt(a.shift(), a.shift());
}else{
g.lt(q, a.shift());
}
}
g.line();
}
}
我想实现一个前端工具,允许用户:
- 在图像上描绘手绘区域
- 跟踪必须关闭
- 跟踪应对齐到网格(例如 32 x 32 像素)
我发现这个 JS 库 (http://ianli.com/sketchpad/) 看起来非常好并且符合要求。
我想没有现成的库专门满足我的需要,所以请随时就如何解决这个问题提出任何建议。
最后的 objective 是输出一个 JSON(或其他坐标列表)映射到图像上的一个 ara,但具有 32 x 32 像素 "resolution"。
跟踪的最终结果应如下所示:
我希望你会发现这个脚本有用。在 JSFiddle 上试用。按 Enter
键选择选项。
window.addEventListener('load', function(){
var O = {
doc: document,
body: document.body,
wd: window,
ce: function(a, b){
return a.appendChild(a = O.doc.createElement(b)), a;
},
cc: function(){
var c, g, gn;
c = O.ce(O.body, 'canvas');
c.width = O.w;
c.height = O.h;
g = c.getContext('2d');
gn = {
sfc: function(a){ // Set fill color
g.fillStyle = a;
},
slc: function(a){ // Set line color
g.strokeStyle = a;
},
slw: function(a){ // Set line width
g.lineWidth = a;
},
bp: function(){ // Begin path
g.beginPath();
},
mt: function(a, b){ // Move to
g.moveTo(gn.tp(0, a), gn.tp(1, b));
},
t: { // Transform
x: 0,
y: 0,
sx: 1,
sy: 1
},
tp: function(a, b){ // Tranform point
return O.mfl(a ? b * gn.t.sy + gn.t.y : b * gn.t.sx + gn.t.x) + .5;
},
tps: function(a, b){ // Tranform distance between points
return O.mfl(a ? b * gn.t.sy : b * gn.t.sx);
},
lt: function(a, b){ // Line to
g.lineTo(gn.tp(0, a), gn.tp(1, b));
},
cp: function(){ // Close path
g.closePath();
},
fill: function(){
g.fill();
},
line: function(){
g.stroke();
},
arc: function(a, b, c, d, e, f){
g.arc(gn.tp(0, a), gn.tp(1, b), c, d, e, f);
},
rc: function(a, b, c, d){ // Rectangle
g.rect(gn.tp(0, a) - .5, gn.tp(1, b) - .5, gn.tps(0, c), gn.tps(1, d));
},
frc: function(a, b, c, d){ // Fill rectangle
g.fillRect(gn.tp(0, a) - .5, gn.tp(1, b) - .5, gn.tps(0, c), gn.tps(1, d));
},
st: function(a, b, c, d){
gn.t.x = a;
gn.t.y = b;
gn.t.sx = c;
gn.t.sy = d;
gn.slw(1 / O.min(c, d));
},
cls: function(){
gn.sfc(O.cols.white);
gn.slc(O.cols.black);
gn.frc(0, 0, O.w, O.h);
}
};
gn.cls();
return gn;
},
init: function(){
O.body.style.margin = '0px';
O.body.style.padding = '0px';
O.cols = {
red: O.rgb(1, 0, 0),
green: O.rgb(0, 1, 0),
blue: O.rgb(0, 0, 1),
white: O.rgb(1, 1, 1),
black: O.rgb(0, 0, 0),
yellow: O.rgb(1, 1, 0),
lightBlue: O.rgb(0, 1, 1),
purple: O.rgb(1, 0, 1),
gray: O.rgb(.5, .5, .5)
};
O.w = O.wd.innerWidth;
O.h = O.wd.innerHeight;
O.wh = O.w / 2;
O.hh = O.h / 2;
O.pih = O.pi / 2;
O.pi2 = O.pi * 2;
main(O);
},
rf: function(b, c){
var a = new XMLHttpRequest();
a.open('get', b, true);
a.onreadystatechange = function(){
a.readyState != 4 || a.status && a.status != 200 || c(a.responseText);
};
a.send();
},
ca: function(a, b){
return (Array(a) + '').split(',').map(b);
},
pad: function(a, b, c){
return (a += '').length >= b ? a : Array(b - a.length + 1).join(c) + a;
},
rgb: function(a, b, c){
return '#' + O.rgbf(a) + O.rgbf(b) + O.rgbf(c);
},
rgbf: function(a){
return O.pad(O.mro(a * 255).toString(16), 2, '0');
},
cols: null,
ael: function(a, b){
O.wd.addEventListener(a, b);
},
rel: function(a, b){
O.wd.removeEventListener(a, b);
},
w: null,
h: null,
raf: function(a){ // Request animation frame
O.wd.requestAnimationFrame(a);
},
rand: function(a){ // Random integer
return O.mfl(O.mra * a);
},
randf: function(a){ // Random float
return O.mra() * a;
},
rq: function(a, b){ // Require
O.rf('/' + a + '/1.js', function(a){
eval(a);
b();
});
},
wh: null,
hh: null,
pi: Math.PI,
pih: null,
pi2: null,
mfl: function(a){ // Math floor
return Math.floor(a);
},
mra: function(){ // Math random
return Math.random();
},
mro: function(a){ // Math round
return Math.round(a);
},
mce: function(a){ // Math ceil
return Math.ceil(a);
},
max: function(a, b){
return Math.max(a, b);
},
min: function(a, b){
return Math.min(a, b);
}
};
O.init();
});
function main(O){
var gridX = 32;
var gridY = 32;
var coords, pressed, cx, cy, g;
(function(){
g = O.cc();
coords = [];
pressed = 0;
O.ael('keydown', onKeyDown);
O.ael('mousedown', onMouseDown);
O.ael('mousemove', onMouseMove);
O.ael('mouseup', onMouseUp);
})();
function onKeyDown(a){
switch(a.keyCode){
case 13:
switch(+prompt('0 - Clear\n1 - Get JSON\n2 - Paste JSON')){
case 0:
coords.length = 0;
g.cls();
break;
case 1:
prompt('', JSON.stringify(coords));
break;
case 2:
coords = JSON.parse(prompt('Pase JSON:'));
drawCoords();
break;
}
break;
}
}
function onMouseDown(a){
if(!a.button){
cx = snapToGrid(0, a.clientX);
cy = snapToGrid(1, a.clientY);
pressed = 1;
}
}
function onMouseMove(a){
var x, y;
if(pressed){
x = snapToGrid(0, a.clientX);
y = snapToGrid(1, a.clientY);
if(x != cx){
g.bp();
g.mt(cx, cy);
g.lt(x, cy);
g.line();
cx = x;
coords.push(cx, cy);
}
if(y != cy){
g.bp();
g.mt(cx, cy);
g.lt(cx, y);
g.line();
cy = y;
coords.push(cx, cy);
}
}
}
function onMouseUp(a){
if(!a.button){
pressed = 0;
coords.push(null);
}
}
function snapToGrid(a, b){
return a ? O.mro(b / gridY) * gridY : O.mro(b / gridX) * gridX;
}
function drawCoords(){
var a = coords.slice(), q;
g.cls();
g.bp();
g.mt(a.shift(), a.shift());
while(a.length){
if((q = a.shift()) == null){
g.mt(a.shift(), a.shift());
}else{
g.lt(q, a.shift());
}
}
g.line();
}
}