Я создаю проект с Jquery, где я должен перетащить курсор на изображение. Каждый курсор связан с комментарием. Таким образом, проект должен сделать точный комментарий к изображению с помощью курсора.
Когда диалоговое окно комментария закрыто, когда можно перетащить курсор, чтобы переместить его более точно. Проблема: когда я перетаскиваю это, это работает, но я не могу перетащить его еще раз, и я не понимаю, почему, вы можете помочь мне это исправить? Спасибо заранее.
Вот код HTML file basic_editor. html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Commentaires</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" media="screen">
<style type="text/css" media="all">@import "lib/imgNotes.css";</style>
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript" src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/jquery-mousewheel@3.1.13"></script>
<script type="text/javascript" src="lib/hammer.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/jquery-hammerjs@2.0.0"></script>
<script type="text/javascript" src="lib/imgViewer.js"></script>
<script type="text/javascript" src="lib/imgNotes.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes" />
</head>
<body>
<table cellspacing="0" cellpadding="0" border="0" style="width: 100%; min-width: 320px;">
<tr>
<td style="padding: 10px">
<h1 align="center" >Placez des points :</h1>
<div align="center">
<img id="image" src="https://1348661504.rsc.cdn77.org/.uc/ib17e91ea01021fb00d004d8fbc022a47b1c90eecedf90701c489025e0380/at-wqaxd7zcc5rsc8m8yhg.png" width="600px" /><br/>
<!-- <img id="image" src="https://www.ecrion.com/wp-content/uploads/2018/06/1-Purchase-Order-Letter.png" width="600px" /><br/> -->
</div>
</td>
</tr>
<tr>
<td style="padding: 10px">
<div align="center">
<button id="export">Exporter</button> <button id="clear">Tout effacer</button>
</div>
</td>
</tr>
<tr>
<td style="text-align:center; padding: 10px">
<div id=txt align="center"></div>
</td>
</tr>
</table>
<script type="text/javascript">
;(function($) {
var notes = null;
$(window).load(function() {
var $img = $("#image").imgNotes({
onEdit: function(ev, elem) {
var $elem = $(elem);
$('#NoteDialog').remove();
return $('<div id="NoteDialog"></div>').dialog({
title: "Commentaire",
resizable: false,
modal: true,
height: "300",
width: "450",
position: { my: "left bottom", at: "right top", of: elem},
buttons: {
"Sauvegarder": function() {
var txt = $('textarea', this).val();
$elem.data("note").note = txt;
$(this).dialog("close");
},
"Supprimer": function() {
$elem.trigger("remove");
$(this).dialog("close");
},
"Annuler": function() {
$(this).dialog("close");
}
},
open: function() {
$(this).css("overflow", "hidden");
var textarea = $('<textarea id="txt" style="height:100%; width:100%;">');
$(this).html(textarea);
textarea.val($elem.data("note").note);
}
});
}
});
$img.imgNotes("import", notes);
var $export = $("#export");
$export.on("click", function() {
var $table = $("<table/>").addClass("gridtable");
var notes = $img.imgNotes('export');
$table.append("<th>X</th><th>Y</th><th>NOTE</th>");
$.each(notes, function(index, item) {
$table.append("<tr><td>" + item.x + "</td><td>" + item.y + "</td><td>" + item.note + "</td></tr>");
});
$('#txt').html($table);
});
var $clear = $("#clear");
$clear.on("click", function() {
$img.imgNotes('clear');
});
});
})(jQuery);
</script>
</body>
</html>
Вот код из javascript file imgNotes. js:
/*
* imgNotes
*
*
* Copyright (c) 2017 Wayne Mogg
* Licensed under the MIT license.
*/
var curseurThis = null;
;(function($) {
$.widget("wgm.imgNotes", $.wgm.imgViewer, {
options: {
canEdit: true,
vAll: "middle",
hAll: "middle",
onEdit: $.noop,
onShow: $.noop,
/*
* Default callback to create a DOM element to indicate a note location
* See the examples for more elaborate alternatives.
*/
onAdd: function() {
this.options.vAll = "bottom";
this.options.hAll = "middle";
var elem = $(document.createElement('div')).addClass("marker").append($('<p class="marker-text">'+(this.notes.length+1)+'</p>'))
.prepend($('<img>',{src: './lib/images/marker-15.svg', width:'110%'})).attr("title","")
.draggable({
start: function() {
},
drag: function() {
},
stop: function(ev, ui) {
var pos = ui.helper.position();
if(curseurThis != null){
ev.preventDefault();
var self = curseurThis;
var rpos = self.cursorToImg(ev.pageX, ev.pageY);
elem.data("note").y = rpos.y;
elem.data("note").x = rpos.x;
self.options.onUpdateMarker.call(self, this);
}
}
});
$(elem).tooltip({
content: function() {
return $(elem).data("note").note;
},
show: false,
hide: {delay:700},
position: {
within: $(this.view),
collision: "flipfit"
}
});
return elem;
},
/*
* Default callback when the markers are repainted
*/
onUpdateMarker: function(elem) {
const compensX = -0.5; // Compensation par rapport à la taille du curseur sur axe X
const compensY = 11; // Compensation par rapport à la taille du curseur sur axe Y
var $elem = $(elem),
note = $elem.data("note");
var pos = this.imgToView(note.x, note.y);
if (pos) {
$elem.css({
left: (pos.x+compensX - $elem.data("xOffset")),
top: (pos.y+compensY - $elem.data("yOffset")),
position: "absolute"
});
}
},
/*
* Default callback when the image view is repainted
*/
onUpdate: function() {
var self = this;
$.each(this.notes, function() {
self.options.onUpdateMarker.call(self, this);
});
}
},
_create: function() {
// the note/marker elements
this.notes = [];
var self = this;
curseurThis = this;
this.options.onClick = function(ev) {
if (self.options.canEdit) {
ev.preventDefault();
var rpos = self.cursorToImg(ev.pageX, ev.pageY);
if (rpos) {
var elem = self.addNote({x: rpos.x, y: rpos.y, note: ""});
self.options.onEdit.call(self, ev, elem);
}
}
};
this._super();
},
_destroy: function() {
this.clear();
this._super();
},
/*
* Add a note
*/
addNote: function(note) {
var self = this,
elem = this.options.onAdd.call(this, note),
$elem = $(elem);
$(this.view).append(elem);
$elem.data("note", note);
switch (this.options.vAll) {
case "top": $elem.data("yOffset", 0); break;
case "bottom": $elem.data("yOffset", $elem.height()); break;
case "middle": $elem.data("yOffset", Math.round($elem.height()/2)); break;
default: $elem.data("yOffset", 0);
}
switch (this.options.hAll) {
case "left": $elem.data("xOffset", 0); break;
case "right": $elem.data("xOffset", $elem.width()); break;
case "middle": $elem.data("xOffset", Math.round($elem.width()/2)); break;
default: $elem.data("xOffset", 0);
}
$elem.click(function(ev) {
ev.preventDefault();
if (self.options.canEdit) {
self.options.onEdit.call(self, ev, elem);
} else {
self.options.onShow.call( self, ev, elem);
}
});
$elem.on("remove", function() {
self._delete(elem);
});
this.notes.push(elem);
this.update();
return elem;
},
/*
* Number of notes
*/
count: function() {
return this.notes.length;
},
/*
* Delete a note
*/
_delete: function(elem) {
this.noteCount--;
this.notes = this.notes.filter(function(v) { return v!== elem; });
$(elem).off();
$(elem).remove();
this.update();
},
/*
* Clear all notes
*/
clear: function() {
var num = this.notes.length;
for ( var i = 0; i < num; i++ ){
var elem = this.notes[i];
elem.off();
elem.remove();
}
this.notes=[];
},
/*
* Add notes from a javascript array
*/
import: function(notes) {
if (this.ready) {
var self = this;
$.each(notes, function() {
self.addNote(this);
});
}
},
/*
* Export notes to an array
*/
export: function() {
var notes = [];
$.each(this.notes, function() {
var note = $(this).data("note");
notes.push(note);
});
return notes;
}
});
})(jQuery);
Вот код из javascript file imgViewer. js:
/*
* imgViewer
*
*
* Copyright (c) 2013 Wayne Mogg
* Licensed under the MIT license.
*/
var waitForFinalEvent = (function () {
var timers = {};
return function (callback, ms, uniqueId) {
if (!uniqueId) {
uniqueId = "Don't call this twice without a uniqueId";
}
if (timers[uniqueId]) {
clearTimeout (timers[uniqueId]);
}
timers[uniqueId] = setTimeout(callback, ms);
};
})();
/*
* imgViewer plugin starts here
*/
;(function($) {
$.widget("wgm.imgViewer", {
options: {
zoomStep: 0.1,
zoom: 1,
zoomMax: undefined,
zoomable: true,
dragable: true,
onReady: $.noop,
onClick: $.noop,
onUpdate: $.noop
},
_create: function() {
var self = this;
if (!this.element.is("img")) {
$.error('imgviewer plugin can only be applied to img elements');
}
// the original img element
self.img = self.element[0];
var $img = $(self.img);
/*
* a copy of the original image to be positioned over it and manipulated to
* provide zoom and pan
*/
self.zimg = $("<img />", {"src": self.img.src}).appendTo("body").wrap("<div class='viewport' />");
var $zimg = $(self.zimg);
// the container or viewport for the image view
self.view = $(self.zimg).parent();
var $view = $(self.view);
// the pixel coordinate of the original image at the center of the viewport
self.vCenter = {};
// a flag used to decide if a mouse click is part of a drag or a proper click
self.drag = false;
self.pinch = false;
// a flag used to check the target image has loaded
self.ready = false;
$img.one("load",function() {
// get and some geometry information about the image
self.ready = true;
var width = $img.width(),
height = $img.height(),
offset = $img.offset();
// cache the image padding information
self.offsetPadding = {
top: parseInt($img.css('padding-top'),10),
left: parseInt($img.css('padding-left'),10),
right: parseInt($img.css('padding-right'),10),
bottom: parseInt($img.css('padding-bottom'),10)
};
/*
* cache the image margin/border size information
* because of IE8 limitations left and right borders are assumed to be the same width
* and likewise top and bottom borders
*/
self.offsetBorder = {
x: Math.round(($img.outerWidth()-$img.innerWidth())/2),
y: Math.round(($img.outerHeight()-$img.innerHeight())/2)
};
/*
* define the css style for the view container using absolute positioning to
* put it directly over the original image
*/
var vTop = offset.top + self.offsetBorder.y + self.offsetPadding.top,
vLeft = offset.left + self.offsetBorder.x + self.offsetPadding.left;
$view.css({
position: "absolute",
overflow: "hidden",
top: vTop+"px",
left: vLeft+"px",
width: width+"px",
height: height+"px"
});
// the zoom and pan image is position relative to the view container
$zimg.css({
position: "relative",
top: 0+"px",
left: 0+"px",
width: width+"px",
height: height+"px",
"-webkit-tap-highlight-color": "transparent"
});
// the initial view is centered at the orignal image
self.vCenter = {
x: width/2,
y: height/2
};
self.update();
}).each(function() {
if (this.complete) { $(this).trigger("load"); }
});
/*
* Render loop code during dragging and scaling using requestAnimationFrame
*/
self.render = false;
/*
* Event handlers
*/
$zimg.hammer();
if (self.options.zoomable) {
self._bind_zoom_events();
}
if (self.options.dragable) {
self._bind_drag_events();
}
$zimg.on("tap", function(ev) {
ev.preventDefault();
if (!self.dragging) {
var scoff = self._get_scroll_offset();
ev.pageX = ev.gesture.center.x + scoff.x;
ev.pageY = ev.gesture.center.y + scoff.y;
self.options.onClick.call(self, ev);
}
});
/*
* Window resize handler
*/
$(window).resize(function() {
self._view_resize();
waitForFinalEvent(function(){
self._view_resize();
}, 300, $img[0].id);
});
self._view_resize();
self.options.onReady.call(self);
},
/*
* Return the window scroll offset - required to convert Hammer.js event coords to page locations
*/
_get_scroll_offset: function() {
var sx,sy;
if (window.scrollX === undefined) {
if (window.pageXOffset === undefined) {
sx = document.documentElement.scrollLeft;
sy = document.documentElement.scrollTop;
} else {
sx = window.pageXOffset;
sy = window.pageYOffset;
}
} else {
sx = window.scrollX;
sy = window.scrollY;
}
return {x: sx, y: sy};
},
/*
* View resize - the aim is to keep the view centered on the same location in the original image
*/
_view_resize: function() {
if (this.ready) {
var $view = $(this.view),
$img = $(this.img),
width = $img.width(),
height = $img.height(),
offset = $img.offset(),
vTop = Math.round(offset.top + this.offsetBorder.y + this.offsetPadding.top),
vLeft = Math.round(offset.left + this.offsetBorder.x + this.offsetPadding.left);
this.vCenter.x *=$img.width()/$view.width();
this.vCenter.y *= $img.height()/$view.height();
$view.css({
top: vTop+"px",
left: vLeft+"px",
width: width+"px",
height: height+"px"
});
this.update();
}
},
/*
* Bind events
*/
_bind_zoom_events: function() {
var self = this;
var $zimg = $(self.zimg);
function doRender() {
if (self.render) {
window.requestAnimationFrame(doRender);
self.update();
}
}
function startRenderLoop() {
if (!self.render) {
self.render = true;
doRender();
}
}
function stopRenderLoop() {
self.render = false;
}
$zimg.on("mousewheel", function(ev) {
ev.preventDefault();
var delta = ev.deltaY ;
self.options.zoom += delta * self.options.zoomStep;
self.update();
});
$zimg.on("touchmove", function(e) {
e.preventDefault();
// e.stopPropagation();
});
$zimg.data("hammer").recognizers[1].options.enable = true;
$zimg.on("pinchstart", function() {
});
$zimg.on("pinch", function(ev) {
ev.preventDefault();
if (!self.pinch) {
var scoff = self._get_scroll_offset();
self.pinchstart = { x: ev.gesture.center.x+scoff.x, y: ev.gesture.center.y+scoff.y};
self.pinchstartrelpos = self.cursorToImg(self.pinchstart.x, self.pinchstart.y);
self.pinchstart_scale = self.options.zoom;
startRenderLoop();
self.pinch = true;
} else {
self.options.zoom = ev.gesture.scale * self.pinchstart_scale;
var npos = self.imgToCursor( self.pinchstartrelpos.x, self.pinchstartrelpos.y);
self.vCenter.x = self.vCenter.x + (npos.x - self.pinchstart.x)/self.options.zoom;
self.vCenter.y = self.vCenter.y + (npos.y - self.pinchstart.y)/self.options.zoom;
}
});
$zimg.on("pinchend", function(ev) {
ev.preventDefault();
if (self.pinch) {
stopRenderLoop();
self.update();
self.pinch = false;
}
});
},
_bind_drag_events: function() {
var self = this;
var $zimg = $(self.zimg);
function doRender() {
if (self.render) {
window.requestAnimationFrame(doRender);
self.update();
}
}
function startRenderLoop() {
if (!self.render) {
self.render = true;
doRender();
}
}
function stopRenderLoop() {
self.render = false;
}
$zimg.on("mousedown", function(e) {
e.preventDefault();
});
$zimg.on("panstart", function() {
});
$zimg.on("panmove", function(ev) {
ev.preventDefault();
if (!self.drag) {
self.drag = true;
self.dragXorg = self.vCenter.x;
self.dragYorg = self.vCenter.y;
startRenderLoop();
} else {
self.vCenter.x = self.dragXorg - ev.gesture.deltaX/self.options.zoom;
self.vCenter.y = self.dragYorg - ev.gesture.deltaY/self.options.zoom;
}
});
$zimg.on( "panend", function(ev) {
ev.preventDefault();
if (self.drag) {
self.drag = false;
stopRenderLoop();
self.update();
}
});
},
/*
* Unbind events
*/
_unbind_zoom_events: function() {
var self = this;
var $zimg = $(self.zimg);
$zimg.data("hammer").recognizers[1].options.enable = false;
$zimg.off("mousewheel");
$zimg.off("pinchstart");
$zimg.off("pinch");
$zimg.off("pinchend");
},
_unbind_drag_events: function() {
var self = this;
var $zimg = $(self.zimg);
$zimg.off("pan");
$zimg.off("panend");
},
/*
* Remove the plugin
*/
destroy: function() {
var $zimg = $(this.zimg);
$zimg.unbind("click");
$(window).unbind("resize");
$zimg.remove();
$(this.view).remove();
$.Widget.prototype.destroy.call(this);
},
_setOption: function(key, value) {
switch(key) {
case 'zoom':
if (parseFloat(value) < 1 || isNaN(parseFloat(value))) {
return;
}
break;
case 'zoomStep':
if (parseFloat(value) <= 0 || isNaN(parseFloat(value))) {
return;
}
break;
case 'zoomMax':
if (parseFloat(value) < 1 || isNaN(parseFloat(value))) {
return;
}
break;
}
var version = $.ui.version.split('.');
if (version[0] > 1 || version[1] > 8) {
this._super(key, value);
} else {
$.Widget.prototype._setOption.apply(this, arguments);
}
switch(key) {
case 'zoom':
if (this.ready) {
this.update();
}
break;
case 'zoomable':
if (this.options.zoomable) {
this._bind_zoom_events();
} else {
this._unbind_zoom_events();
}
break;
case 'dragable':
if (this.options.dragable) {
this._bind_drag_events();
} else {
this._unbind_drag_events();
}
break;
case 'zoomMax':
if (this.ready) {
this._view_resize();
this.update();
}
break;
}
},
addElem: function(elem) {
$(this.view).append(elem);
},
/*
* Test if a relative image coordinate is visible in the current view
*/
isVisible: function(relx, rely) {
var view = this.getView();
if (view) {
return (relx >= view.left && relx <= view.right && rely >= view.top && rely <= view.bottom);
} else {
return false;
}
},
/*
* Get relative image coordinates of current view
*/
getView: function() {
if (this.ready) {
var $img = $(this.img),
width = $img.width(),
height = $img.height(),
zoom = this.options.zoom;
return {
top: this.vCenter.y/height - 0.5/zoom,
left: this.vCenter.x/width - 0.5/zoom,
bottom: this.vCenter.y/height + 0.5/zoom,
right: this.vCenter.x/width + 0.5/zoom
};
} else {
return null;
}
},
/*
* Pan the view to be centred at the given relative image location
*/
panTo: function(relx, rely) {
if ( this.ready && relx >= 0 && relx <= 1 && rely >= 0 && rely <=1 ) {
var $img = $(this.img),
width = $img.width(),
height = $img.height();
this.vCenter.x = relx * width;
this.vCenter.y = rely * height;
this.update();
return { x: this.vCenter.x/width, y: this.vCenter.y/height };
} else {
return null;
}
},
/*
* Convert a relative image location to a viewport pixel location
*/
imgToView: function(relx, rely) {
if ( this.ready && relx >= 0 && relx <= 1 && rely >= 0 && rely <=1 ) {
var $img = $(this.img),
width = $img.width(),
height = $img.height();
var zLeft = width/2 - this.vCenter.x * this.options.zoom;
var zTop = height/2 - this.vCenter.y * this.options.zoom;
var vx = relx * width * this.options.zoom + zLeft;
var vy = rely * height * this.options.zoom + zTop;
return { x: Math.round(vx), y: Math.round(vy) };
} else {
return null;
}
},
/*
* Convert a relative image location to a page pixel location
*/
imgToCursor: function(relx, rely) {
var pos = this.imgToView(relx, rely);
if (pos) {
var offset = $(this.img).offset();
pos.x += offset.left + this.offsetBorder.x + this.offsetPadding.left;
pos.y += offset.top + this.offsetBorder.y + this.offsetPadding.top;
return pos;
} else {
return null;
}
},
/*
* Convert a viewport pixel location to a relative image coordinate
*/
viewToImg: function(vx, vy) {
if (this.ready) {
var $img = $(this.img),
width = $img.width(),
height = $img.height();
var zLeft = width/2 - this.vCenter.x * this.options.zoom;
var zTop = height/2 - this.vCenter.y * this.options.zoom;
var relx= (vx - zLeft)/(width * this.options.zoom);
var rely = (vy - zTop)/(height * this.options.zoom);
if (relx>=0 && relx<=1 && rely>=0 && rely<=1) {
return {x: relx, y: rely};
} else {
return null;
}
} else {
return null;
}
},
/*
* Convert a page pixel location to a relative image coordinate
*/
cursorToImg: function(cx, cy) {
if (this.ready) {
var $img = $(this.img),
width = $img.width(),
height = $img.height(),
offset = $img.offset();
var zLeft = width/2 - this.vCenter.x * this.options.zoom;
var zTop = height/2 - this.vCenter.y * this.options.zoom;
var relx = (cx - offset.left - this.offsetBorder.x - this.offsetPadding.left- zLeft)/(width * this.options.zoom);
var rely = (cy - offset.top - this.offsetBorder.y - this.offsetPadding.top - zTop)/(height * this.options.zoom);
if (relx>=0 && relx<=1 && rely>=0 && rely<=1) {
return {x: relx, y: rely};
} else {
return null;
}
} else {
return null;
}
},
/*
* Convert relative image coordinate to Image pixel
*/
relposToImage: function(pos) {
if (this.ready) {
var img = this.img,
width = img.naturalWidth,
height = img.naturalHeight;
return {x: Math.round(pos.x*width), y: Math.round(pos.y*height)};
} else {
return null;
}
},
/*
* Adjust the display of the image
*/
update: function() {
if (this.ready) {
var zTop, zLeft, zWidth, zHeight,
$img = $(this.img),
width = $img.width(),
height = $img.height(),
// offset = $img.offset(),
zoom = this.options.zoom,
zoomMax = this.options.zoomMax,
half_width = width/2,
half_height = height/2;
zoom = zoomMax === undefined ? zoom : Math.min(zoom, zoomMax);
this.options.zoom = zoom;
if (zoom <= 1) {
zTop = 0;
zLeft = 0;
zWidth = width;
zHeight = height;
this.vCenter = {
x: half_width,
y: half_height
};
this.options.zoom = 1;
zoom = 1;
} else {
zTop = Math.round(half_height - this.vCenter.y * zoom);
zLeft = Math.round(half_width - this.vCenter.x * zoom);
zWidth = Math.round(width * zoom);
zHeight = Math.round(height * zoom);
/*
* adjust the view center so the image edges snap to the edge of the view
*/
if (zLeft > 0) {
this.vCenter.x = half_width/zoom;
zLeft = 0;
} else if (zLeft+zWidth < width) {
this.vCenter.x = width - half_width/zoom ;
zLeft = width - zWidth;
}
if (zTop > 0) {
this.vCenter.y = half_height/zoom;
zTop = 0;
} else if (zTop + zHeight < height) {
this.vCenter.y = height - half_height/zoom;
zTop = height - zHeight;
}
}
$(this.zimg).css({
width: width+"px",
height: height+"px"
});
var xt = -(this.vCenter.x - half_width)*zoom;
var yt = -(this.vCenter.y - half_height)*zoom;
$(this.zimg).css({transform: "translate(" + xt + "px," + yt + "px) scale(" + zoom + "," + zoom + ")" });
/*
* define the onUpdate option to do something after the image is redisplayed
* probably shouldn't pass out the this object - need to think of something better
*/
this.options.onUpdate.call(this);
}
}
});
})(jQuery);
Если хотите, это ссылка на базу моего проекта, это проект другого человека, который загрузил его на github, но он не содержит мои изменения. Возможно, это поможет вам: введите описание ссылки здесь