Canvas 文本点击无效。怎么修?
Canvas text click not working. How to fix?
JS代码-
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function print(log) {
console.log(log)
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace
ctx.strokeRect(x, y, width, height);
}
}
function addNewText(string_text, arrayname) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
addNewText("Hello world", "text 1");
所以我听从了@Kaiido 对 BBox 的建议,并将 textBaseline 更改为“top”。非常适合。现在,当我在我的帮助论坛下 运行 他的代码剪接器时,它似乎可以工作并打印“found”(这表明它可以工作)。当我 运行 它时,它似乎不起作用。这可能是什么原因。
他的代码有效:https://gyazo.com/511bf35523fcb3ea8a26c2b088530f99
我的编码不工作:https://gyazo.com/743c38f6a33a7f1f4513bac361c23588
HTML代码-
<div id="middle_container">
<div class="center_container">
<canvas id="canvas"></canvas>
</div>
</div>
CSS代码-
#canvas {
height: 667px;
width: 800px;
touch-action: auto;
cursor: inherit;
visibility: visible;
border: 1px solid grey;
}
你的问题的关键在于:
console.log("t:", texts.length); // empty array
//empty array so does nothing
for (var i = 0; i < texts.length; i++) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB = canvas.getBoundingClientRect();
console.log("BB:", BB);
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "16px verdana";
text.width = ctx.measureText(text.text).width;
text.height = 16;
texts.push(text);
draw();
}
function hitDrag(x, y, textIndex) {
var r = texts[textIndex];
return (x > r.x && x < r.x + r.width && y > r.y && y < r.y + r.height);
}
function myDown(e) {
console.log("called");
e.preventDefault();
e.stopPropagation();
console.log("c2");
mx = parseInt(e.clientX - offsetX);
my = parseInt(e.clientY - offsetY);
console.log("t:", texts.length); // empty array
//empty array so does nothing
for (var i = 0; i < texts.length; i++) {
console.log("i:", i);
if (hitDrag(mx, my, i)) {
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
console.log("triggered");
myDown(e);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
问题是您使用的是默认 textBaseline = "alphabetic"
,这使得 y
值对应于 o
.
等字形的底部
您可以通过跟踪看到您的文本BBox是错误的:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 40 + 40;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x, y, width, height);
}
}
addNewText("Hello world", "text 1");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
将此 textBaseline
设置为 "top"
将以较低的成本将问题最小化,但这不是完美的选择,而且如果您使用特殊字形。
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 40 + 40;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x, y, width, height);
}
}
addNewText("Hello world", "text 1");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
各大浏览器终于支持TextMetrics接口的actualBoundingBoxXXX
属性,所以我们现在可以得到精确的文本BBox:
function getTextBBox( ctx, text ) {
const metrics = ctx.measureText( text );
const left = metrics.actualBoundingBoxLeft * -1;
const top = metrics.actualBoundingBoxAscent * -1;
const right = metrics.actualBoundingBoxRight;
const bottom = metrics.actualBoundingBoxDescent;
const width = right - left;
const height = bottom - top;
return { left, top, right, bottom, width, height };
}
function getTextBBox(ctx, text) {
const metrics = ctx.measureText(text);
const left = metrics.actualBoundingBoxLeft * -1;
const top = metrics.actualBoundingBoxAscent * -1;
const right = metrics.actualBoundingBoxRight;
const bottom = metrics.actualBoundingBoxDescent;
const width = right - left;
const height = bottom - top;
return { left, top, right, bottom, width, height };
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
ctx.font = "32px verdana";
ctx.textBaseline = "top";
const bbox = getTextBBox(ctx, string_text);
const prevText = texts[ texts.length - 1 ];
const text = {
text: string_text,
x: 20,
y: (prevText ? (prevText.y + prevText.bbox.bottom) : 32) + 2,
bbox,
name: arrayname
};
texts.push(text);
draw();
}
function hitDrag(mx,my,textIndex) {
const { x, y, bbox: { left, right, top, bottom } } = texts[textIndex];
return (
mx > x + left &&
mx < x + right &&
my > y + top &&
my < y + bottom
);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, bbox } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x + bbox.left, y + bbox.top, bbox.width, bbox.height);
}
}
addNewText("Hello world", "text 1");
addNewText("Works fine?", "text 2");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
现在,如果您需要查找是否单击了字形的实际绘制像素,则需要获取 canvas 的 ImageData,我已经在 [=] 中展示了如何执行此操作23=],这里不再赘述。
JS代码-
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
function print(log) {
console.log(log)
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace
ctx.strokeRect(x, y, width, height);
}
}
function addNewText(string_text, arrayname) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
addNewText("Hello world", "text 1");
所以我听从了@Kaiido 对 BBox 的建议,并将 textBaseline 更改为“top”。非常适合。现在,当我在我的帮助论坛下 运行 他的代码剪接器时,它似乎可以工作并打印“found”(这表明它可以工作)。当我 运行 它时,它似乎不起作用。这可能是什么原因。
他的代码有效:https://gyazo.com/511bf35523fcb3ea8a26c2b088530f99
我的编码不工作:https://gyazo.com/743c38f6a33a7f1f4513bac361c23588
HTML代码-
<div id="middle_container">
<div class="center_container">
<canvas id="canvas"></canvas>
</div>
</div>
CSS代码-
#canvas {
height: 667px;
width: 800px;
touch-action: auto;
cursor: inherit;
visibility: visible;
border: 1px solid grey;
}
你的问题的关键在于:
console.log("t:", texts.length); // empty array
//empty array so does nothing
for (var i = 0; i < texts.length; i++) {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB = canvas.getBoundingClientRect();
console.log("BB:", BB);
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 20 + 20;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "16px verdana";
text.width = ctx.measureText(text.text).width;
text.height = 16;
texts.push(text);
draw();
}
function hitDrag(x, y, textIndex) {
var r = texts[textIndex];
return (x > r.x && x < r.x + r.width && y > r.y && y < r.y + r.height);
}
function myDown(e) {
console.log("called");
e.preventDefault();
e.stopPropagation();
console.log("c2");
mx = parseInt(e.clientX - offsetX);
my = parseInt(e.clientY - offsetY);
console.log("t:", texts.length); // empty array
//empty array so does nothing
for (var i = 0; i < texts.length; i++) {
console.log("i:", i);
if (hitDrag(mx, my, i)) {
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
console.log("triggered");
myDown(e);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
问题是您使用的是默认 textBaseline = "alphabetic"
,这使得 y
值对应于 o
.
您可以通过跟踪看到您的文本BBox是错误的:
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 40 + 40;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x, y, width, height);
}
}
addNewText("Hello world", "text 1");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
将此 textBaseline
设置为 "top"
将以较低的成本将问题最小化,但这不是完美的选择,而且如果您使用特殊字形。
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
var y = texts.length * 40 + 40;
var text = {
text: string_text,
x: 20,
y: y,
name: arrayname
};
ctx.font = "32px verdana";
ctx.textBaseline = "top";
text.width = ctx.measureText(text.text).width;
text.height = 32;
texts.push(text);
draw();
}
function hitDrag(x,y,textIndex) {
var r=texts[textIndex];
return (x>r.x && x<r.x+r.width && y>r.y && y<r.y+r.height);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, width, height } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x, y, width, height);
}
}
addNewText("Hello world", "text 1");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
各大浏览器终于支持TextMetrics接口的actualBoundingBoxXXX
属性,所以我们现在可以得到精确的文本BBox:
function getTextBBox( ctx, text ) {
const metrics = ctx.measureText( text );
const left = metrics.actualBoundingBoxLeft * -1;
const top = metrics.actualBoundingBoxAscent * -1;
const right = metrics.actualBoundingBoxRight;
const bottom = metrics.actualBoundingBoxDescent;
const width = right - left;
const height = bottom - top;
return { left, top, right, bottom, width, height };
}
function getTextBBox(ctx, text) {
const metrics = ctx.measureText(text);
const left = metrics.actualBoundingBoxLeft * -1;
const top = metrics.actualBoundingBoxAscent * -1;
const right = metrics.actualBoundingBoxRight;
const bottom = metrics.actualBoundingBoxDescent;
const width = right - left;
const height = bottom - top;
return { left, top, right, bottom, width, height };
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var $canvas = $("#canvas");
var BB=canvas.getBoundingClientRect();
var offsetX = BB.left;
var offsetY = BB.top;
var mx;
var my;
var texts = [];
var images = [];
var dragF = -1;
var mode = "none";
var print = console.log;
function addNewText(string_text, arrayname) {
ctx.font = "32px verdana";
ctx.textBaseline = "top";
const bbox = getTextBBox(ctx, string_text);
const prevText = texts[ texts.length - 1 ];
const text = {
text: string_text,
x: 20,
y: (prevText ? (prevText.y + prevText.bbox.bottom) : 32) + 2,
bbox,
name: arrayname
};
texts.push(text);
draw();
}
function hitDrag(mx,my,textIndex) {
const { x, y, bbox: { left, right, top, bottom } } = texts[textIndex];
return (
mx > x + left &&
mx < x + right &&
my > y + top &&
my < y + bottom
);
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
mx=parseInt(e.clientX-offsetX);
my=parseInt(e.clientY-offsetY);
for(var i=0;i<texts.length;i++){
if(hitDrag(mx,my,i)){
print("found");
dragF = i;
}
}
}
$("#canvas").mousedown(function(e) {
myDown(e);
});
function draw() {
for(const { text, x, y, bbox } of texts) {
ctx.fillText(text, x, y);
// trace the text's BBox
ctx.strokeRect(x + bbox.left, y + bbox.top, bbox.width, bbox.height);
}
}
addNewText("Hello world", "text 1");
addNewText("Works fine?", "text 2");
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="canvas"></canvas>
现在,如果您需要查找是否单击了字形的实际绘制像素,则需要获取 canvas 的 ImageData,我已经在 [=] 中展示了如何执行此操作23=],这里不再赘述。