画布文本单击不起作用。怎么修?

Posted

技术标签:

【中文标题】画布文本单击不起作用。怎么修?【英文标题】:Canvas text click not working. How to fix? 【发布时间】:2022-01-17 07:52:56 【问题描述】:

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");

所以我在 BBox 上遵循了@Kaiido 的建议,并将 textBaseline 更改为“top”。非常适合。现在,当我在我的帮助论坛下运行他的代码剪辑器时,它似乎可以工作并打印“找到”(这表明它可以工作)。当我运行它时,它似乎不起作用。这可能是什么原因。

他的代码有效: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;
    

【问题讨论】:

我建议 var print = console.log; 改为 var myprint = function(mything)console.log(mything);; 之类的,而不是重新定义 window.print() 请为您正在使用的$("#canvas") 添加 HTML 您好,首先感谢您的帮助!数组不是空的,因为 addNewText 函数将一个对象添加到数组中。所以假设已经通过 addNewText 函数添加了对象,它仍然没有检测到点击。 调用那个函数 - addNewText("charlie""delta"); 它是 0 长度。也许将您的问题更新为更完整的示例? 我们需要看到draw。从外观上看,您没有设置上下文的 textBaseline,因此 BBox 的 y 轴偏离了一个行高。如果您想要一个更防弹的功能来检测画布中的文本点击:***.com/a/67015797/3702797 【参考方案1】:

问题是您使用的是默认的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>

现在,如果您需要查找是否单击了字形的实际绘制像素,则需要获取画布的 ImageData,我已经在 an other answer 中展示了如何执行此操作,所以我不会在这里重复一遍。

【讨论】:

您好,首先感谢您的帮助@Kaiido |当我测试它时,您的代码 sn-p 的问题似乎已解决,但它仍然对我不起作用。您能否查看它,并检查可能是什么原因。我包含了 GIF 的链接! (抱歉重复评论,我不知道在哪里评论,因为我是新手) 您现在面对的是***.com/questions/2588181/…,最好在我的回答下评论我的回答。 非常感谢!你帮了很多忙。祝你有美好的一天,伙计。【参考方案2】:

问题的关键在这里:

 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>

【讨论】:

他们可能会调用addNewText,它将填充这个数组并在上下文中绘制文本,否则我想他们的问题会是,“为什么没有绘制文本?”,而不是“为什么单击绘制的文本时没有任何反应?” 我更新并添加了绘图功能到主论坛。当我单击绘制的文本时,我只想让它在控制台日志中打印一些东西,所以我知道它可以工作。 您好,首先感谢您的帮助@Kaiido |当我测试它时,您的代码 sn-p 的问题似乎已解决,但它仍然对我不起作用。您能否查看它,并检查可能是什么原因。我包含了 GIF 的链接!

以上是关于画布文本单击不起作用。怎么修?的主要内容,如果未能解决你的问题,请参考以下文章

EaselJS StageGL:文本不起作用(但使用舞台画布)

亚马逊 App Store 上的应用内购买多年来都不起作用。怎么修?

Quickblox:基于带有“+”字符的字段值获取自定义对象似乎不起作用?怎么修?

如何修复子字符串在 vue.js 3 中不起作用?

Adobe Animate 画布播放()不起作用

使用 img.crossOrigin = "Anonymous" 将图像绘制到画布不起作用