画布视图框窗口内的可点击对象或形状

Posted

技术标签:

【中文标题】画布视图框窗口内的可点击对象或形状【英文标题】:clickable object or shape inside a canvas viewbox window 【发布时间】:2021-01-01 09:38:42 【问题描述】:

<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>

<style>
body 
  font-family: "Lato", sans-serif;


.sidenav 
  height: 100%;
  width: 0;
  position: fixed;
  z-index: 1;
  top: 0;
  left: 0;
  background-color: #111;
  overflow-x: hidden;
  transition: 0.5s;
  padding-top: 60px;


.sidenav a 
  padding: 8px 8px 8px 32px;
  text-decoration: none;
  font-size: 25px;
  color: #818181;
  display: block;
  transition: 0.3s;


.sidenav a:hover 
  color: #f1f1f1;


.sidenav .closebtn 
  position: absolute;
  top: 0;
  right: 25px;
  font-size: 36px;
  margin-left: 50px;


#main 
  transition: margin-left .5s;
  padding: 16px;


@media screen and (max-height: 450px) 
  .sidenav padding-top: 15px;
  .sidenav a font-size: 18px;

</style>
</head>
<body>

<div id="mySidenav" class="sidenav">
  <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a> 
  <a href="#">About</a>
  <a href="#">Services</a>
  <a href="#">Clients</a>
  <a href="#">Contact</a>
    
</div>
<div id="main">
  <span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776; Menu</span>
<canvas id="canvas"   style="border:2px solid #d3d3d3;">></canvas>
</div>

<script>

var zoomIntensity = 0.1;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = 90;
var height = 50;
var scale = 1;
var originx = 0;
var originy = 0;
var visibleWidth = width;
var visibleHeight = height;


var c = document.getElementById("canvas");
var ctx = c.getContext("2d");

function draw()

    context.fillStyle = "white";
    context.fillRect(originx,originy,1000/scale,800/scale);

    context.fillStyle = "blue";
    context.fillRect(170,50,50,50);
    context.fillStyle = "white";
    context.fillText('click here',175,70);   


setInterval(draw, 1000/60);
canvas.onwheel = function (event)
    event.preventDefault();
    var mousex = event.clientX - canvas.offsetLeft;
    var mousey = event.clientY - canvas.offsetTop;
    var wheel = event.deltaY < 0 ? 1 : -1;
    var zoom = Math.exp(wheel*zoomIntensity);
    
    context.translate(originx, originy);
    originx -= mousex/(scale*zoom) - mousex/scale;
    originy -= mousey/(scale*zoom) - mousey/scale;
    context.scale(zoom, zoom);
    context.translate(-originx, -originy);
    scale *= zoom;
    visibleWidth = width / scale;
    visibleHeight = height / scale;

function openNav() 
  document.getElementById("mySidenav").style.width = "200px";
  document.getElementById("main").style.marginLeft = "200px";


function closeNav() 
  document.getElementById("mySidenav").style.width = "0";
  document.getElementById("main").style.marginLeft= "0";

</script>

</body>
</html>

是否可以在画布框架或视图框上制作可点击的形状或对象,即使我放大或缩小,因为我看到的所有示例都只是一个固定的可点击位置。例如,当我放大时谷歌地图位置,我可以点击更多对象。 是否可以在画布框架或视图框上制作可点击的形状或对象,即使我放大或缩小,因为我看到的所有示例都只是一个固定的可点击位置。例如,当我放大谷歌地图位置时,我可以点击更多对象。

【问题讨论】:

我认为这可能会有所帮助Jsfiddle 【参考方案1】:

您必须考虑两个空间,实际画布空间和翻译空间。这意味着您需要转换鼠标坐标才能知道对象实际位于空间中的哪个位置,并将其映射回画布坐标,这样您就可以知道您是否正在单击它。这是通过跟踪您在var originx 中提供的偏移值来实现的,但问题是,如果您使用context.translate(originx, originy),至少没有办法根据我的轨迹翻译鼠标单击位置。缩放和平移对象空间。

我在不使用 translate 函数的情况下重写了代码,创建了我自己的 translate 函数,该函数使您能够在画布空间和对象空间之间进行转换,以便在对象上注册点击事件,而不管平移或放大位置。

此功能还具有单击平移功能,因此您可以单击对象屏幕并将其拖动到要放置对象的任何位置。

/*jshint esversion: 8 */

(function() 
  const canvas = document.querySelector("canvas");
  const context = canvas.getContext("2d");
  let canvasWidth = 600;
  let canvasHeight = 600;
  let offset = 
    x: 0,
    y: 0
  ;
  let pan = 
    x: 0,
    y: 0
  ;
  var zoomIntensity = 0.1;
  let scale = 1;
  let mouse = 
    x: 0,
    y: 0
  ;
  let dragging = false;
  let square;

  let shapes = [
      x: 170,
      y: 50,
      w: 50,
      h: 50,
      color: "blue"
    ,
    
      x: 85,
      y: 25,
      w: 50,
      h: 50,
      color: "red"
    ,
    
      x: 50,
      y: 100,
      w: 50,
      h: 50,
      color: "green"
    ,
  ];

  function init() 
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    renderCanvas();
  

  function drawSquare(x, y, width, height, color) 
    context.fillStyle = color;
    context.fillRect(x, y, width, height);
  

  function objectsToCanvasScreen(x, y) 
    const canvasScreenX = Math.floor((x - offset.x) * scale);
    const canvasScreenY = Math.floor((y - offset.y) * scale);

    return 
      x: canvasScreenX,
      y: canvasScreenY
    ;
  

  function canvasToObjectsScreen(x, y) 
    return 
      x: x / scale + offset.x,
      y: y / scale + offset.y
    ;
  

  function renderCanvas() 
    context.clearRect(0, 0, canvas.width, canvas.height);

    shapes.forEach((
      x,
      y,
      w,
      h,
      color
    , index) => 
      const 
        x: csx,
        y: csy
       = objectsToCanvasScreen(x, y);
      shapes[index]._x = csx;
      shapes[index]._y = csy;
      shapes[index]._w = w * scale;
      shapes[index]._h = h * scale;
      drawSquare(csx, csy, w * scale, h * scale, color);
    );

  

  canvas.onwheel = function(e) 
    const zoom = Math.exp((e.deltaY < 0 ? 1 : -1) * zoomIntensity);
    const beforeZoom = canvasToObjectsScreen(mouse.x, mouse.y);
    scale *= zoom;
    const afterZoom = canvasToObjectsScreen(mouse.x, mouse.y);
    offset.x += beforeZoom.x - afterZoom.x;
    offset.y += beforeZoom.y - afterZoom.y;

    renderCanvas();
  ;
  canvas.onmousedown = function(e) 
    if (e.button === 0) 
      pan.x = mouse.x;
      pan.y = mouse.y;
      dragging = true;
    
  ;
  canvas.onmouseup = (e) => (dragging = false);
  canvas.onmousemove = function(e) 
    mouse.x = e.offsetX;
    mouse.y = e.offsetY;
    if (dragging) 
      offset.x -= (mouse.x - pan.x) / scale;
      offset.y -= (mouse.y - pan.y) / scale;

      pan.x = mouse.x;
      pan.y = mouse.y;
      renderCanvas();
    
  ;
  canvas.onclick = function(e) 
    shapes.forEach((
      _x,
      _y,
      _w,
      _h,
      color
    ) => 
      const 
        x: mousex,
        y: mousey
       = canvasToObjectsScreen(mouse.x, mouse.y);
      const 
        x: squarex,
        y: squarey
       = canvasToObjectsScreen(_x, _y);
      if (
        mousex >= squarex &&
        mousex <= _w / scale + squarex &&
        mousey >= squarey &&
        mousey <= _h / scale + squarey
      ) 
        alert(`$color clicked!`);
      
    );

  ;

  init();
)();
body 
  font-family: "Lato", sans-serif;


#canvas 
  background: #E1BC8B;
<body>
  <div id="main">
    <canvas id="canvas"  ></canvas>
  </div>
</body>

【讨论】:

谢谢,这正是我想要的,但我可以再问一个问题/如何添加更多对象 创建一个数组来保存对象列表,更新 renderCanvas 函数以在执行缩放或拖动等画布事件时循环此列表。在画布单击事件处理程序中,当单击某处时循环遍历对象列表并检查它是否是被单击的对象 有很多问题要问,但我是新手,所以你能改变以前的代码吗..,谢谢

以上是关于画布视图框窗口内的可点击对象或形状的主要内容,如果未能解决你的问题,请参考以下文章

android中EditText内的可点击按钮(或任何视图)

tkinter canvas create_window

使用Android在xml中使用ShapeDrawable绘制多个形状

Fabric.js 将画布(或对象组)剪辑到多边形

用渐变(或纯色)填充特定的可绘制矢量

许多画布对象导致“InvalidStateError:尝试使用不可用或不再可用的对象”