img-marker
Version:
一个基于 canvas 的简单轻量级的图片标注库,支持多种图形标注 🚀🚀🚀
662 lines (661 loc) • 22.1 kB
JavaScript
/*! *****************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
var t = function(e, i) {
return (
(t =
Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array &&
function(t, e) {
t.__proto__ = e;
}) ||
function(t, e) {
for (var i in e)
Object.prototype.hasOwnProperty.call(e, i) && (t[i] = e[i]);
}),
t(e, i)
);
};
var e = function() {
return (
(e =
Object.assign ||
function(t) {
for (var e, i = 1, s = arguments.length; i < s; i++)
for (var n in (e = arguments[i]))
Object.prototype.hasOwnProperty.call(e, n) && (t[n] = e[n]);
return t;
}),
e.apply(this, arguments)
);
};
function i(t, e, i) {
if (i || 2 === arguments.length)
for (var s, n = 0, a = e.length; n < a; n++)
(!s && n in e) ||
(s || (s = Array.prototype.slice.call(e, 0, n)), (s[n] = e[n]));
return t.concat(s || e);
}
var s,
n,
a,
o = new ((function() {
function t() {
this.eventTree = new Map();
}
return (
(t.prototype.on = function(t, e) {
var s,
n = null !== (s = this.eventTree.get(t)) && void 0 !== s ? s : [];
this.eventTree.set(t, i(i([], n, !0), [e], !1));
}),
(t.prototype.emit = function(t) {
for (var e, i = [], s = 1; s < arguments.length; s++)
i[s - 1] = arguments[s];
for (
var n = 0,
a = null !== (e = this.eventTree.get(t)) && void 0 !== e ? e : [];
n < a.length;
n++
) {
a[n].apply(null, i);
}
}),
(t.prototype.off = function(t, e) {
var i,
s = null !== (i = this.eventTree.get(t)) && void 0 !== i ? i : [],
n = s.findIndex(function(t) {
return t === e;
});
s.splice(n, 1), this.eventTree.set(t, s);
}),
t
);
})())();
!(function(t) {
t[(t.rect = 1)] = "rect";
})(s || (s = {})),
(function(t) {
(t[(t.edit = 0)] = "edit"), (t[(t.rect = 1)] = "rect");
})(n || (n = {})),
(function(t) {
(t.Add = "add"),
(t.Select = "select"),
(t.Load = "load"),
(t.Update = "update");
})(a || (a = {}));
var r = function() {
var t;
return null === (t = window.navigator) || void 0 === t
? void 0
: t.userAgent.includes("Mobile");
},
h = (function(e) {
function i(t) {
var i = e.call(this, t) || this;
return (i.type = s.rect), (i.type = s.rect), i;
}
return (
(function(e, i) {
if ("function" != typeof i && null !== i)
throw new TypeError(
"Class extends value " + String(i) + " is not a constructor or null"
);
function s() {
this.constructor = e;
}
t(e, i),
(e.prototype =
null === i
? Object.create(i)
: ((s.prototype = i.prototype), new s()));
})(i, e),
Object.defineProperty(i.prototype, "ctrlsData", {
get: function() {
var t = this.coor,
e = t[0],
i = e[0],
s = e[1],
n = t[1],
a = n[0],
o = n[1];
return [
[i, s],
[i + (a - i) / 2, s],
[a, s],
[a, s + (o - s) / 2],
[a, o],
[i + (a - i) / 2, o],
[i, o],
[i, s + (o - s) / 2],
];
},
enumerable: !1,
configurable: !0,
}),
(i.MIN_WIDTH = 10),
(i.MIN_HEIGHT = 10),
i
);
})(function(t) {
(this.coor = []),
(this.strokeStyle = "#FFE729"),
(this.fillStyle = "rgba(255,231,41, 0.2)"),
(this.lineWidth = 4),
(this.type = s.rect),
(this.active = !1),
(this.creating = !1),
(this.dragging = !1),
(this.uuid = (function() {
if ("object" == typeof crypto && "function" == typeof crypto.randomUUID)
return crypto.randomUUID();
var t = new Date().getTime();
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(
e
) {
var i = 16 * Math.random();
return (
(i = (t + i) % 16 | 0),
(t = Math.floor(t / 16)),
("x" === e ? i : (3 & i) | 8).toString(16)
);
});
})()),
Object.assign(this, t);
}),
c = (function() {
function t(t, e) {
(this.CANVAS_WIDTH = 0),
(this.CANVAS_HEIGHT = 0),
(this.IMAGE_ORIGIN_WIDTH = 0),
(this.IMAGE_ORIGIN_HEIGHT = 0),
(this.IMAGE_WIDTH = 0),
(this.IMAGE_HEIGHT = 0),
(this.strokeStyle = "#FFE729"),
(this.fillStyle = "rgba(255,231,41, 0.2)"),
(this.lineWidth = 4),
(this.activeStrokeStyle = "#FFE729"),
(this.activeFillStyle = "rgba(255,231,41, 0.2)"),
(this.ctrlStrokeStyle = "#505E72"),
(this.ctrlFillStyle = "#fff"),
(this.ctrlRadius = 4),
(this.dataset = []),
(this.image = new Image()),
(this.currentMode = n.edit),
(this.ctrlIndex = -1),
(this.currentMousePoint = [0, 0]),
(this.handleLoad = this.handleLoad.bind(this)),
(this.handleMouseDown = this.handleMouseDown.bind(this)),
(this.handelMouseMove = (function(t, e) {
void 0 === e && (e = 200);
var i = Date.now();
return function() {
for (var s = [], n = 0; n < arguments.length; n++)
s[n] = arguments[n];
if (Date.now() - i >= e)
return (i = Date.now()), t.apply(void 0, s);
};
})(this.handelMouseMove.bind(this), 16.7)),
(this.handelMouseUp = this.handelMouseUp.bind(this)),
(this.handelKeyup = this.handelKeyup.bind(this));
var i = "string" == typeof t ? document.querySelector(t) : t;
i instanceof HTMLCanvasElement
? ((this.canvas = i),
this.initSetting(),
this.initEvents(),
e && this.setImage(e))
: console.warn("HTMLCanvasElement is required!");
}
return (
Object.defineProperty(t.prototype, "activeShape", {
get: function() {
var t;
return null !==
(t = this.dataset.find(function(t) {
return t.active;
})) && void 0 !== t
? t
: null;
},
enumerable: !1,
configurable: !0,
}),
(t.prototype.initSetting = function() {
(this.ctx = this.ctx || this.canvas.getContext("2d", { alpha: !0 })),
(this.CANVAS_WIDTH = this.canvas.clientWidth),
(this.CANVAS_HEIGHT = this.canvas.clientHeight),
(this.canvas.width = 1 * this.CANVAS_WIDTH),
(this.canvas.height = 1 * this.CANVAS_HEIGHT),
(this.canvas.style.width = this.CANVAS_WIDTH + "px"),
(this.canvas.style.height = this.CANVAS_HEIGHT + "px"),
this.ctx.scale(1, 1);
}),
(t.prototype.initEvents = function() {
this.image.addEventListener("load", this.handleLoad),
this.canvas.addEventListener("mousedown", this.handleMouseDown),
this.canvas.addEventListener("mousemove", this.handelMouseMove),
this.canvas.addEventListener("mouseup", this.handelMouseUp),
document.body.addEventListener("keyup", this.handelKeyup);
}),
(t.prototype.destroy = function() {
this.image.removeEventListener("load", this.handleLoad),
this.canvas.removeEventListener("mousedown", this.handleMouseDown),
this.canvas.removeEventListener("mousemove", this.handelMouseMove),
this.canvas.removeEventListener("mouseup", this.handelMouseUp),
document.body.removeEventListener("keyup", this.handelKeyup);
}),
(t.prototype.handleLoad = function() {
(this.IMAGE_ORIGIN_WIDTH = this.IMAGE_WIDTH = this.image.width),
(this.IMAGE_ORIGIN_HEIGHT = this.IMAGE_HEIGHT = this.image.height),
this.fitZoom(),
this.update();
}),
(t.prototype.handleMouseDown = function(t) {
var e,
i,
s,
c = this;
t.stopPropagation();
var u = this.mergeEvent(t),
l = u.mouseX,
p = u.mouseY,
d = u.mouseCX,
v = u.mouseCY,
f = r() && 2 === t.touches.length ? [d, v] : [l, p];
if ((!r() && 1 === t.buttons) || (r() && 1 === t.touches.length)) {
this.currentMousePoint = f;
var y =
null !==
(i =
null === (e = this.activeShape) || void 0 === e
? void 0
: e.ctrlsData) && void 0 !== i
? i
: [];
if (
((this.ctrlIndex = y.findIndex(function(t) {
return c.isPointInCircle(f, t, c.ctrlRadius);
})),
this.ctrlIndex > -1)
)
return void this.changeCursor("nwse-resize");
if (this.currentMode > n.edit) {
var I = void 0,
g = [l, p];
this.currentMode,
n.rect,
((I = new h({ coor: [g, g] })).creating = !0),
this.dataset.forEach(function(t) {
t.active = !1;
}),
(I.active = !0),
this.dataset.push(I);
} else {
var S = this.isHitOnShape(f),
x = S.isOnShape,
H = S.shape,
M = S.index;
x
? ((null === (s = this.activeShape) || void 0 === s
? void 0
: s.uuid) !== H.uuid && o.emit(a.Select, H),
this.dataset.map(function(t) {
return (t.active = t.uuid === H.uuid);
}),
(H.dragging = !0),
this.dataset.splice(M, 1),
this.dataset.push(H),
this.changeCursor("move"))
: this.activeShape && (this.activeShape.active = !1);
}
this.update();
}
}),
(t.prototype.handelMouseMove = function(t) {
var e;
t.stopPropagation();
var i = this.mergeEvent(t),
a = i.mouseX,
o = i.mouseY,
c = i.mouseCX,
u = i.mouseCY,
l = r() && 2 === t.touches.length ? [c, u] : [a, o],
p = this.isHitOnShape(l),
d = p.isOnShape,
v = p.shape;
if (
(this.ctrlIndex > -1
? this.changeCursor("nwse-resize")
: this.currentMode > n.edit
? this.changeCursor("copy")
: d
? this.changeCursor(v.active ? "move" : "pointer")
: this.changeCursor("default"),
((!r() && 1 === t.buttons) || (r() && 1 === t.touches.length)) &&
(null === (e = this.activeShape) || void 0 === e ? void 0 : e.type))
) {
if (this.ctrlIndex > -1) {
if (this.activeShape.type === s.rect) {
var f = this.activeShape.coor,
y = f[0],
I = y[0],
g = y[1],
S = f[1],
x = S[0],
H = S[1],
M = [];
switch (this.ctrlIndex) {
case 0:
M = [
[a, o],
[x, H],
];
break;
case 1:
M = [
[I, o],
[x, H],
];
break;
case 2:
M = [
[I, o],
[a, H],
];
break;
case 3:
M = [
[I, g],
[a, H],
];
break;
case 4:
M = [
[I, g],
[a, o],
];
break;
case 5:
M = [
[I, g],
[x, o],
];
break;
case 6:
M = [
[a, g],
[x, o],
];
break;
case 7:
M = [
[a, g],
[x, H],
];
}
var m = M[0],
A = m[0],
E = m[1],
_ = M[1],
T = _[0],
C = _[1];
T - A >= h.MIN_WIDTH &&
C - E >= h.MIN_HEIGHT &&
(this.activeShape.coor = [
[A, E],
[T, C],
]);
}
} else if (this.activeShape.dragging) {
M = [];
for (var G = !0, w = 0; w < this.activeShape.coor.length; w++) {
var b = this.activeShape.coor[w],
D = b[0],
N = b[1],
W = D + a - this.currentMousePoint[0],
k = N + o - this.currentMousePoint[1];
(W < 0 ||
W > this.CANVAS_WIDTH ||
k < 0 ||
k > this.CANVAS_HEIGHT) &&
(G = !1),
M.push([W, k]);
}
G && (this.activeShape.coor = M);
} else
this.activeShape.creating &&
(this.changeCursor("nwse-resize"),
this.activeShape.type === s.rect &&
this.activeShape.coor.splice(1, 1, [a, o]));
(this.currentMousePoint = l), this.update();
}
}),
(t.prototype.handelMouseUp = function(t) {
var e;
if (
(t.stopPropagation(),
(this.ctrlIndex = -1),
(null === (e = this.activeShape) || void 0 === e ? void 0 : e.type) &&
((this.activeShape.dragging = !1), this.activeShape.creating))
) {
if (this.activeShape.type === s.rect) {
var i = this.activeShape.coor,
n = i[0],
r = n[0],
c = n[1],
u = i[1],
l = u[0],
p = u[1];
Math.abs(r - l) < h.MIN_WIDTH || Math.abs(c - p) < h.MIN_HEIGHT
? this.dataset.pop()
: ((this.activeShape.coor = [
[Math.min(r, l), Math.min(c, p)],
[Math.max(r, l), Math.max(c, p)],
]),
(this.activeShape.creating = !1));
}
o.emit(a.Add, this.activeShape), this.update();
}
}),
(t.prototype.handelKeyup = function(t) {
var e;
t.stopPropagation(),
(null === (e = this.activeShape) || void 0 === e ? void 0 : e.type) &&
(("Backspace" !== t.key && "Escape" !== t.key) ||
this.deleteByUuid(this.activeShape.uuid));
}),
(t.prototype.fitZoom = function() {
var t = this.CANVAS_WIDTH / this.CANVAS_HEIGHT,
e = this.IMAGE_ORIGIN_WIDTH / this.IMAGE_ORIGIN_HEIGHT;
t > e
? ((this.IMAGE_HEIGHT = this.CANVAS_HEIGHT),
(this.IMAGE_WIDTH = this.CANVAS_HEIGHT * e))
: ((this.IMAGE_WIDTH = this.CANVAS_WIDTH),
(this.IMAGE_HEIGHT = this.CANVAS_WIDTH / e));
}),
(t.prototype.setData = function(t) {
(this.dataset = t.map(function(t) {
return t.type, s.rect, new h(t);
})),
this.update();
}),
(t.prototype.setMode = function(t) {
this.currentMode = t;
}),
(t.prototype.on = function(t, e) {
o.on(t, e);
}),
(t.prototype.deleteByUuid = function(t) {
var e = this.dataset.findIndex(function(e) {
return e.uuid === t;
});
e > -1 && (this.dataset.splice(e, 1), this.update());
}),
(t.prototype.update = function() {
this.ctx.save(),
this.ctx.clearRect(0, 0, this.CANVAS_WIDTH, this.CANVAS_HEIGHT),
this.drawShapes(),
this.activeShape &&
[s.rect].includes(this.activeShape.type) &&
this.drawCtrlList(this.activeShape),
this.ctx.restore(),
o.emit(a.Update, this.dataset);
}),
(t.prototype.drawShapes = function() {
for (var t = 0; t < this.dataset.length; t++) {
var e = this.dataset[t];
if (e.type === s.rect) this.drawRect(e);
}
}),
(t.prototype.drawImg = function() {
var t = (this.CANVAS_WIDTH - this.IMAGE_WIDTH) / 2,
e = (this.CANVAS_HEIGHT - this.IMAGE_HEIGHT) / 2;
this.ctx.drawImage(
this.image,
t,
e,
this.IMAGE_WIDTH,
this.IMAGE_HEIGHT
);
}),
(t.prototype.drawRect = function(t) {
if (2 === t.coor.length) {
var e = t.strokeStyle,
i = t.fillStyle,
s = t.active,
n = t.creating,
a = t.coor,
o = t.lineWidth,
r = a[0],
h = r[0],
c = r[1],
u = a[1],
l = u[0],
p = u[1];
this.ctx.save(),
(this.ctx.lineWidth = o || this.lineWidth),
(this.ctx.fillStyle = i || this.fillStyle),
(this.ctx.strokeStyle =
s || n ? this.activeStrokeStyle : e || this.strokeStyle);
var d = l - h,
v = p - c;
this.ctx.fillRect(h, c, d, v),
this.ctx.strokeRect(h, c, d, v),
this.ctx.restore();
}
}),
(t.prototype.drawCtrlList = function(t) {
var e = this;
t.ctrlsData.forEach(function(t, i) {
e.drawCtrl(t);
});
}),
(t.prototype.drawCtrl = function(t) {
var e = t[0],
i = t[1];
this.ctx.save(),
this.ctx.beginPath(),
(this.ctx.fillStyle = this.ctrlFillStyle),
(this.ctx.strokeStyle = this.ctrlStrokeStyle),
this.ctx.arc(e, i, this.ctrlRadius, 0, 2 * Math.PI, !0),
this.ctx.fill(),
this.ctx.arc(e, i, this.ctrlRadius, 0, 2 * Math.PI, !0),
this.ctx.stroke(),
this.ctx.restore();
}),
(t.prototype.setImage = function(t) {
(this.image.src = t),
(this.image.crossOrigin = "anonymous"),
(this.canvas.style.backgroundImage = 'url("'.concat(t, '")')),
(this.canvas.style.backgroundSize = "contain"),
(this.canvas.style.backgroundRepeat = "no-repeat"),
(this.canvas.style.backgroundPosition = "center center"),
(this.image.onload = function() {
o.emit(a.Load);
});
}),
(t.prototype.exportImg = function(t, e) {
return (
void 0 === t && (t = "image/png"),
void 0 === e && (e = 1),
this.activeShape && (this.activeShape.active = !1),
this.ctx.save(),
this.ctx.clearRect(0, 0, this.CANVAS_WIDTH, this.CANVAS_HEIGHT),
(this.ctx.fillStyle = "#fff"),
this.ctx.fillRect(0, 0, this.CANVAS_WIDTH, this.CANVAS_HEIGHT),
this.drawImg(),
this.drawShapes(),
this.ctx.restore(),
this.canvas.toDataURL(t, e)
);
}),
(t.prototype.mergeEvent = function(t) {
var i = 0,
s = 0,
n = 0,
a = 0;
if (r()) {
var o = t.touches[0],
h = o.clientX,
c = o.clientY,
u = t.target.getBoundingClientRect(),
l = u.left,
p = u.top;
if (
((i = Math.round(h - l)),
(s = Math.round(c - p)),
2 === t.touches.length)
) {
var d = t.touches[1] || {},
v = d.clientX,
f = void 0 === v ? 0 : v,
y = d.clientY,
I = void 0 === y ? 0 : y;
(n = Math.round(Math.abs((f - h) / 2 + h) - l)),
(a = Math.round(Math.abs((I - c) / 2 + c) - p));
}
} else (i = t.offsetX), (s = t.offsetY);
return e(e({}, t), { mouseX: i, mouseY: s, mouseCX: n, mouseCY: a });
}),
(t.prototype.isPointInCircle = function(t, e, i) {
var s = t[0],
n = t[1],
a = e[0],
o = e[1];
return Math.sqrt(Math.pow(a - s, 2) + Math.pow(o - n, 2)) <= i;
}),
(t.prototype.isPointInRect = function(t, e) {
var i = t[0],
s = t[1],
n = e[0],
a = n[0],
o = n[1],
r = e[1],
h = r[0],
c = r[1];
return a <= i && i <= h && o <= s && s <= c;
}),
(t.prototype.isHitOnShape = function(t) {
for (var e = this.dataset.length - 1; e >= 0; e--) {
var i = this.dataset[e];
if (i.type === s.rect && this.isPointInRect(t, i.coor))
return { isOnShape: !0, index: e, shape: i };
}
return { isOnShape: !1, index: -1, shape: null };
}),
(t.prototype.changeCursor = function(t) {
this.canvas.style.cursor = t;
}),
t
);
})();
export { a as EventType, n as MarkMode, s as ShapeType, c as default };