如何使放置在fabricjs对象顶部的DOM元素即使在水平滚动时也保持不变



我有一个画布,它有一个对象数组,这些对象被定位并添加到画布中。每个对象都有一个匹配的DOM元素,用于触发工具提示。此元素正好放置在画布对象的顶部。

在桌面上,这很好,因为背景图像总是充满屏幕,并且没有滚动条。但在手机上,我有一个水平滚动条,这样用户就可以在图像上左右滚动(否则会变得很小(。

问题是,位于结构对象顶部的DOM元素根据对象所在的位置保持在其位置,而不进行任何滚动,当我水平滚动时,DOM元素保持在相同的位置。

我在手机上制作了一段视频,显示了以下内容:https://streamable.com/xn1t2i带圆圈的点是放置在画布对象上的画布外的DOM元素(没有圆圈的蓝色点(。

因此,我想到了以下解决方案:将整个脚本放在一个函数中,并在一个事件上调用该函数,例如:touchmove。然而,这非常慢,并且在移动时会显示很多闪烁。所以我尝试了touchend,但这也很慢,而且在单击工具提示时也会触发该函数。

touchmove的示例视频:https://streamable.com/708d2s正如你所看到的,点确实会重新定位,但速度太慢,如果滚动拖得太长,点就会再次错位。

我也尝试过scroll,但根本不起作用。

这是我目前的代码:

javascript:

(function() {
function reRender(){
var myImg = document.querySelector("#background");
if(window.outerWidth > 767) {
menuheightcanvas = 172.5;
var realWidth = window.innerWidth;
var realHeight = myImg.naturalHeight;
}else{
menuheightcanvas = 99.8;
var realWidth = myImg.naturalWidth - 900;
var realHeight = myImg.naturalHeight;
}
var source = document.getElementById('background').src;
var canvas = new fabric.Canvas('c', {
allowTouchScrolling: true,
selection: false
});
canvas.allowTouchScrolling = true;
canvas.hoverCursor = 'pointer';
canvas.setDimensions({
width: realWidth,
height: realHeight
});
var img = new Image();
// use a load callback to add image to canvas.
img.src = 'https://printzelf.nl/new/assets/images/custom/WOONKAMER.jpg';
fabric.Object.NUM_FRACTION_DIGITS = 10;
fabric.Image.fromURL(source, function(img) {
img.scaleToWidth(canvas.width);
canvas.setBackgroundImage(img);
canvas.requestRenderAll();
});
var scaleToWidth = realWidth / myImg.width;
// alert (scaleToWidth)
const hotspots = [{
top: (140* scaleToWidth),
left: (720* scaleToWidth),
radius: 10,
fill: '#009fe3',
id: 'cirkel1',
hoverCursor: 'pointer',
selectable: false,
imgtop: 71,
imgleft: 236,
imgheight: 335,
imgwidth: 514,
placement: 'right',
tooltipid: 'cirkel1',
imgUrl: 'https://printzelf.nl/new/cms/images/canvas/woonkamer/gordijnen.jpg'
},
{
top: (160* scaleToWidth),
left: (640* scaleToWidth),
radius: 10,
fill: '#009fe3',
id: 'cirkel2',
hoverCursor: 'pointer',
selectable: false,
imgtop: 82,
imgleft: 351,
imgheight: 313,
imgwidth: 337,
placement: 'right',
tooltipid: 'cirkel2',
imgUrl: 'https://printzelf.nl/new/cms/images/canvas/woonkamer/voile.jpg'
},
{
top: (350* scaleToWidth),
left: (120* scaleToWidth),
radius: 10,
fill: '#009fe3',
id: 'cirkel3',
hoverCursor: 'pointer',
selectable: false,
imgtop: 293,
imgleft: 21,
placement: 'right',
imgheight: 81,
imgwidth: 107,
imgUrl: 'https://printzelf.nl/new/cms/images/canvas/woonkamer/fotoblok.jpg'
},
{
top: (275* scaleToWidth),
left: (165* scaleToWidth),
radius: 10,
fill: '#009fe3',
id: 'cirkel4',
hoverCursor: 'pointer',
selectable: false,
imgtop: 283,
imgleft: 127,
placement: 'right',
imgheight: 60,
imgwidth: 57,
imgUrl: 'https://printzelf.nl/new/cms/images/canvas/woonkamer/fotopaneel.jpg'
},
{
top: (430* scaleToWidth),
left: (600* scaleToWidth),
radius: 10,
fill: '#009fe3',
id: 'cirkel5',
hoverCursor: 'pointer',
selectable: false,
imgtop: 365,
imgleft: 227,
placement: 'right',
imgheight: 185,
imgwidth: 396,
imgUrl: 'https://printzelf.nl/new/cms/images/canvas/woonkamer/zitzak.jpg'
}
];
const loadedImages = [];
for (let [idx, props] of hotspots.entries()) {
let c = new fabric.Circle(props);
c.class = 'hotspot';
c.name = 'hotspot-' + idx;
canvas.add(c);
}
fabric.Canvas.prototype.getAbsoluteCoords = function(object) {
return {
left: object.left + this._offset.left,
top: object.top + menuheightcanvas
};
}
var btnWidth = 40,
btnHeight = 40;
function positionBtn(obj, index) {
var absCoords = canvas.getAbsoluteCoords(obj);
var element = document.getElementById('cirkel' + index);
element.style.left = (absCoords.left - btnWidth / 10) + 'px';
element.style.top = (absCoords.top - btnHeight / 10) + 'px';
}
canvas.getObjects().forEach(function(ho, index) {
positionBtn(ho, index + 1);
});
$(".canvastip").each(function(i) {
tippy(this, {
theme: 'blue',
allowHTML: true,
placement: 'right',
animation: 'scale-subtle',
interactive: true,
// popperOptions: {
//  strategy: 'fixed',
//  modifiers: [
//      {
//          name: 'flip',
//          options: {
//              fallbackPlacements: ['bottom', 'bottom'],
//          },
//      },
//      {
//          name: 'preventOverflow',
//          options: {
//              altAxis: true,
//              tether: false,
//          },
//      },
//  ],
// },
onShow(instance) {
canvas.getObjects().forEach(function(ho, index) {
if (ho.class && ho.class === 'hotspot') {
if (instance.id == index + 1) {
// check if image was previously loaded
if (loadedImages.indexOf(ho.name) < 0) {
// image is not in the array
// so it needs to be loaded
// prepare the image properties
let imgProps = {
  width: ho.imgwidth,
  height: ho.imgheight,
  left: ho.imgleft* scaleToWidth,
  top: ho.imgtop* scaleToWidth,
  scaleX: 1* scaleToWidth,
  scaleY: 1* scaleToWidth,
  selectable: false,
  id: 'img-' + ho.name,
  hoverCursor: "default",
};
instance.setProps({placement: ho.placement})
var printzelfImg = new Image();
printzelfImg.onload = function(img) {
  var printzelf = new fabric.Image(printzelfImg, imgProps);
  printzelf.trippyHotspotImage = true;
  canvas.add(printzelf);
};
printzelfImg.src = ho.imgUrl;
// update the `loadedImages` array
loadedImages.push(ho.name);
} else {
for (const o of canvas.getObjects()) {
  if (o.id && o.id === 'img-' + ho.name) {
          o.visible = true;
          break;
  }
}
canvas.renderAll();
}
}
}
});
},
onHide(instance) {
for (const o of canvas.getObjects()) {
if (o.trippyHotspotImage) {
o.visible = false;
}
}
canvas.renderAll();
},
content: function(reference) {
return reference.querySelector('.tooltipcontentcanvas' + (i + 1));
}
});
});
}
window.addEventListener('scroll', reRender, false);
reRender();
})();

HTML:

<img id="background" src="https://printzelf.nl/new/assets/images/custom/WOONKAMER.jpg" alt="" style="display:none;">
<div class="canvas-container" style="width: 100%; position: relative;">
<canvas id="c" width="100%" height="500" class="lower-canvas" style="position: absolute; width: 100%; height: 500px; left: 0px; top: 0px;"></canvas>
</div>
<span id="cirkel1" class="canvastip" style="border-radius:100%;width: 25px;height:25px;position:absolute;cursor:pointer;">
<div class="tooltipcontentcanvas1 tooltipcontentcanvas darktext" style="position:relative;">
<div class="tooltipwrap">
<a href="product/gordijnen" title="Weet je wat ik graag zou willen zijn?.." alt="Weet je wat ik graag zou willen zijn?.." class="tooltipprodlink">
<span class="tooltipprodlink">v.a. <b>€19,36</b> p/m<sup>2</sup></span>
<img class="tooltipimgprod" src="cms/images/canvas/woonkamer/tooltip/gordijnen.jpg" alt="Gordijnen">
</a>
<div class="tooltipinfo">
<span class="toptitle">Gordijnen</span>
<h2>Weet je wat ik graag zou willen zijn?..</h2>
<span class="sub">
<ul>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">10 materialen</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">+ handige accessoires</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">Contourfrezen mogelijk</li>
</ul>
</span>
<a href="product/gordijnen" title="Weet je wat ik graag zou willen zijn?.." alt="Weet je wat ik graag zou willen zijn?.."><span class="btnstyle purplebtn">Stel gordijnen samen</span></a>
</div>
</div>
</div>
</span>
<span id="cirkel2" class="canvastip" style="border-radius:100%;width: 25px;height:25px;position:absolute;cursor:pointer;">
<div class="tooltipcontentcanvas2 tooltipcontentcanvas darktext" style="position:relative;">
<div class="tooltipwrap">
<a href="product/vitrage" title="Jouw vitrage wordt een rage!" alt="Jouw vitrage wordt een rage!" class="tooltipprodlink">
<span class="tooltipprodlink">v.a. <b>€19,36</b> p/m<sup>2</sup></span>
<img class="tooltipimgprod" src="cms/images/canvas/woonkamer/tooltip/vitragegordijnen.jpg" alt="Vitragegordijnen">
</a>
<div class="tooltipinfo">
<span class="toptitle">Vitragegordijnen</span>
<h2>Jouw vitrage wordt een rage!</h2>
<span class="sub">
<ul>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">10 materialen</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">+ handige accessoires</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">Contourfrezen mogelijk</li>
</ul>
</span>
<a href="product/vitrage" title="Jouw vitrage wordt een rage!" alt="Jouw vitrage wordt een rage!"><span class="btnstyle purplebtn">Stel vitragegordijnen samen</span></a>
</div>
</div>
</div>
</span>
<span id="cirkel3" class="canvastip" style="border-radius:100%;width: 25px;height:25px;position:absolute;cursor:pointer;">
<div class="tooltipcontentcanvas3 tooltipcontentcanvas darktext" style="position:relative;">
<div class="tooltipwrap">
<a href="product/fotoblok" title="Dit blok staat als een huis in je huis!" alt="Dit blok staat als een huis in je huis!" class="tooltipprodlink">
<span class="tooltipprodlink">v.a. <b>€19,36</b> p/m<sup>2</sup></span>
<img class="tooltipimgprod" src="cms/images/canvas/woonkamer/tooltip/fotoblok.jpg" alt="Fotoblok">
</a>
<div class="tooltipinfo">
<span class="toptitle">Fotoblok</span>
<h2>Dit blok staat als een huis in je huis!</h2>
<span class="sub">
<ul>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">10 materialen</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">+ handige accessoires</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">Contourfrezen mogelijk</li>
</ul>
</span>
<a href="product/fotoblok" title="Dit blok staat als een huis in je huis!" alt="Dit blok staat als een huis in je huis!"><span class="btnstyle purplebtn">Stel fotoblok samen</span></a>
</div>
</div>
</div>
</span>
<span id="cirkel4" class="canvastip" style="border-radius:100%;width: 25px;height:25px;position:absolute;cursor:pointer;">
<div class="tooltipcontentcanvas4 tooltipcontentcanvas darktext" style="position:relative;">
<div class="tooltipwrap">
<a href="product/foto-op-paneel" title="Zet jouw lievelingsfoto op een paneel" alt="Zet jouw lievelingsfoto op een paneel" class="tooltipprodlink">
<span class="tooltipprodlink">v.a. <b>€19,36</b> p/m<sup>2</sup></span>
<img class="tooltipimgprod" src="cms/images/canvas/woonkamer/tooltip/fotopaneel.jpg" alt="Fotopaneel">
</a>
<div class="tooltipinfo">
<span class="toptitle">Fotopaneel</span>
<h2>Zet je lievelingsfoto op een paneel</h2>
<span class="sub">
<ul>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">10 materialen</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">+ handige accessoires</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">Contourfrezen mogelijk</li>
</ul>
</span>
<a href="product/foto-op-paneel" title="Zet jouw lievelingsfoto op een paneel" alt="Zet jouw lievelingsfoto op een paneel"><span class="btnstyle purplebtn">Stel fotopaneel samen</span></a>
</div>
</div>
</div>
</span>
<span id="cirkel5" class="canvastip" style="border-radius:100%;width: 25px;height:25px;position:absolute;cursor:pointer;">
<div class="tooltipcontentcanvas5 tooltipcontentcanvas darktext" style="position:relative;">
<div class="tooltipwrap">
<a href="product/zitzak" title="Geniet rustig van jouw ontwerp" alt="Geniet rustig van jouw ontwerp" class="tooltipprodlink">
<span class="tooltipprodlink">v.a. <b>€19,36</b> p/m<sup>2</sup></span>
<img class="tooltipimgprod" src="cms/images/canvas/woonkamer/tooltip/zitzak.jpg" alt="Zitzak">
</a>
<div class="tooltipinfo">
<span class="toptitle">Zitzakken</span>
<h2>Geniet rustig van jouw ontwerp</h2>
<span class="sub">
<ul>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">10 materialen</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">+ handige accessoires</li>
<li><img class="vinkje" src="https://printzelf.nl/new/assets/images/custom/vinkje.gif">Contourfrezen mogelijk</li>
</ul>
</span>
<a href="product/zitzak" title="Geniet rustig van jouw ontwerp" alt="Geniet rustig van jouw ontwerp"><span class="btnstyle purplebtn">Stel zitzak samen</span></a>
</div>
</div>
</div>
</span>

整个页面的代码笔:https://codepen.io/twan2020/pen/VwPZmJx也许可以试着在你的手机上查看这个,因为出于某种原因,当在桌面上将屏幕调整为移动大小时,会破坏画布。不过,在我的手机上,它运行良好。

如何确保DOM元素点始终位于画布对象点上?在保持目前速度的同时?

.canvas-container#cirkel1 ... #cirkelNdiv周围包装一个新的相对定位的div,以便相关的html代码区域的结构如下:

<div class="canvas-container-container">
<div class="canvas-container">...</div>
<span id="cirkel1">...</span>
<span id="cirkel2">...</span>
...
</div>

重要的是,.canvas-container-container具有position: relative;,以便相对于.canvas-container-container的原点定位其绝对定位的子#cirkel1 ... #cirkelN。此更改意味着.canvas-container-container的原点对应于画布中的(0,0)点,从而解决了问题的根本问题。

更新您的css代码,使.canvas-container的css规则成为canvas-container-container的规则(确保删除相应的.canvas-containercss代码(:

.canvas-container-container {
position: relative;
height: 500px;
}
@media only screen and (max-width:991px) {
.canvas-container-container{
overflow-x:auto;
overflow-y:hidden;
}
.canvas-container-container,
.canvas-container {
height: 400px;
}
}

这样,水平滚动应该已经开始工作了。然而,由于您的#cirkel1 ... #cirkelN现在相对于.canvas-container-container定位,您将不再需要在getAbsoluteCoords中添加this._offset.leftmenuheightcanvas

fabric.Canvas.prototype.getAbsoluteCoords = function(object) {
return {
left: object.left, // +this._offset.left not needed here
top: object.top, // +menuheightcanvas not needed here
};
}

事实上,您甚至根本不需要这个getAbsoluteCoords,因为obj.left和obj.top已经相对于画布原点,因此不需要偏移:

function positionBtn(obj, index) {
var element = document.getElementById('cirkel' + index);
element.style.left = (obj.left - btnWidth / 10) + 'px';
element.style.top = (obj.top - btnHeight / 10) + 'px';
}

最新更新