#javascript #html #jquery #canvas #fabricjs
Вопрос:
У меня есть холст, на котором есть массив объектов, эти объекты расположены и добавлены на холст. Каждый объект имеет соответствующий элемент DOM, который запускает всплывающую подсказку. Этот элемент помещается точно поверх объекта canvas.
На рабочем столе это работает нормально, так как фоновое изображение всегда заполняет экран, а полосы прокрутки нет. Но на мобильном телефоне у меня есть горизонтальная полоса прокрутки, чтобы пользователи могли прокручивать изображение влево и вправо (иначе оно станет маленьким).
Проблема в том, что элементы 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 amp;amp; 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 amp;amp; 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? Сохраняя при этом ту скорость, которую он имеет в настоящее время?
Комментарии:
1. Есть ли у вас какой-нибудь прогресс в этом деле? обычно, когда я работаю с холстом и у меня много объектов, я использую технику наслоения. У меня нет ноутбука, но примерно через 12 часов я мог бы придумать интересное решение.
2. @danielm2402 Все решения, которые я придумал, к сожалению, ужасно медленные. Прослушиватели событий, которые я пробовал, например. Кроме того, когда вы перетаскиваете экран на мобильном устройстве и отпускаете палец, он немного затягивается, пока движение не остановится, это последнее движение снова приведет к неправильному выравниванию точек.
Ответ №1:
оберните нового родственника, расположенного div
вокруг вашего .canvas-container
и ваших #cirkel1 ... #cirkelN
дивов, так, чтобы соответствующая область 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;
для того, чтобы позиционировать своих «абсолютных» детей #cirkel1 ... #cirkelN
относительно происхождения .canvas-container-container
. Это изменение подразумевает, что начало координат .canvas-container-container
соответствует (0,0)
точке на вашем холсте, что решает основную проблему вашей проблемы.
Обновите свой css-код, чтобы правила css .canvas-container
стали правилами canvas-container-container
(убедитесь, что вы удалили соответствующий .canvas-container
css-код).:
.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
вас, вам больше не нужно будет добавлять this._offset.left
и menuheightcanvas
в свой getAbsoluteCoords
:
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.слева и 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';
}
Комментарии:
1. Спасибо! Отлично работает. Я приму награду, когда смогу. Осталось еще несколько часов.
2. Спасибо за редактирование, я редактировал одновременно, поэтому ваше редактирование было автоматически отклонено из-за конфликта (если я не ошибаюсь). Однако впоследствии я добавил ваше исправление. Всегда рад помочь.
3. Да, я это видел, никаких проблем. У меня есть один вопрос по поводу вашего ответа. Содержимое холста теперь находится относительно контейнера, это также означает, что всплывающие подсказки не отображаются за пределами холста. Есть ли простое решение этой проблемы? Пример: i.gyazo.com/f98274f7c82e1408b33c89886e9bc8c7.png
4. попробуйте отобразить
.canvas-container-container
безoverflow-y: hidden;
. дайте мне знать, если кончики инструментов все еще обрезаны5. Есть альтернатива: увеличьте высоту
.canvas-container-container
так, чтобы она была достаточно большой, чтобы покрыть все ящики с наконечниками инструментов. Сделайте так, чтобы содержимое, приведенное ниже, частично перекрывало контейнер холста с относительным расположением или отрицательным полем. При необходимости удалите полосы прокрутки.canvas-container-container
с помощью css.