I'm working on a image gallery based on OpenSeaDragon, and I'd like to be able to use overlays in collection mode. Based on the various examples on the OSD website (http://openseadragon.github.io/) I managed to hack together a minimal working example, but there are several issues I've not been able to fix (see https://jsfiddle.net/7ox0hg9L/).
First, the on/off overlay toggle works fine, but if I pan/zoom the image the overlay reappears, even though toggle-off deletes the element from the DOM using parentNode.removeChild().
Second, I can't seem to get the overlay tooltips to work consistently on the first page, and they never appear on the following pages. The tooltip on the radiobutton label works fine on any page though, so I'm not sure why the tooltips on the overlays do not.
Any suggestion would be welcome. Please bear in mind that I am new to javascript. Thanks!
EDIT: iangilman's answer below and his edits on jsfiddle put me back on track, and helped me create the gallery I had in mind. I post here the full solution for those who may need similar features. Thanks Ian!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='utf-8'>
<script src="https://cdnjs.cloudflare.com/ajax/libs/openseadragon/2.3.1/openseadragon.min.js"></script>
<style>
body {
margin: 0;
color: #333;
font-family: Helvetica, Arial, FreeSans, san-serif;
background-color: #121621;
}
.openseadragon{
width: 800px;
height: 600px;
border: 1px solid black;
color: #333;
background-color: black;
}
.highlight{
opacity: 0.4;
filter: alpha(opacity=40);
outline: 6px auto #0A7EbE;
background-color: white;
}
.highlight:hover, .highlight:focus{
filter: alpha(opacity=70);
opacity: 0.7;
background-color: transparent;
}
.nav {
cursor: pointer;
display: inline-block;
font-size: 25px;
}
.controls {
text-align: center;
display: table;
background-color: #eee;
table-layout: fixed;
width: 800px;
}
</style>
</head>
<body>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="controls">
<label class="labels"><input id="showOverlays" type="checkbox"><a id="selector" title="">Show overlays</a></label>
<a class="nav previous" title="Previous" id="prv"> < </a>
<a class="nav next" title="Next" id="nxt"> > </a>
</div>
<div id="example-runtime-overlay" class="openseadragon" />
<script type="text/javascript">
var tileSource = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "http://openseadragon.github.io/example-images/highsmith/highsmith_files/",
Format: "jpg",
Overlap: "2",
TileSize: "256",
Size: {
Height: "9221",
Width: "7026"
}
}
};
var runtimeViewer = OpenSeadragon({
id: "example-runtime-overlay",
prefixUrl: "openseadragon/images/",
showSequenceControl: true,
sequenceMode: true,
nextButton: "nxt",
previousButton: "prv",
tileSources: [{
tileSource: tileSource,
overlay: [{
id: 'example-overlay',
x: 0.43,
y: 0.47,
width: 0.15,
height: 0.20,
className: 'highlight',
caption: 'Nice painting'
}]
},{
tileSource: tileSource,
overlay: [{
id: 'example-overlay',
x: 0.65,
y: 0.05,
width: 0.12,
height: 0.12,
className: 'highlight',
caption: 'Milton'
}]
}]
});
var page = 0;
runtimeViewer.addHandler("page", function (data) {
page = data.page;
});
$('.next').click(function() {
radio.prop('checked', false);
});
$('.previous').click(function() {
radio.prop('checked', false);
});
var radio = $('#showOverlays')
.prop('checked', false)
.change(function() {
if (radio.prop('checked')) {
var overlay = runtimeViewer.tileSources[page].overlay[0];
var elt = document.createElement("div");
elt.id = overlay.id;
elt.className = overlay.className;
elt.title = "";
$(elt).tooltip({
content: overlay.caption
});
runtimeViewer.addOverlay({
element: elt,
location: new OpenSeadragon.Rect(overlay.x, overlay.y, overlay.width, overlay.height)
});
} else {
var overlay = runtimeViewer.tileSources[page].overlay[0];
var element = document.getElementById(overlay.id);
if (element) {
runtimeViewer.removeOverlay(element);
delete element;
}
}
});
$(function() {
$(document).tooltip();
});
</script>
</body>
</html>
Looks like you're off to a good start!
You're correctly adding the overlays with addOverlay, so you need to remove them with removeOverlay:
runtimeViewer.removeOverlay(element);
For the tooltips, unfortunately OpenSeadragon's event handling can interfere with jQuery, so you'll have to use the OpenSeadragon MouseTracker:
function bindTooltip(elt) {
new OpenSeadragon.MouseTracker({
element: elt,
enterHandler: function(event) {
// Show tooltip
},
exitHandler: function(event) {
// Hide tooltip
}
}).setTracking(true);
}
Related
I need help please ! :D
I can't find how to change the cursor when dragging with the "SortableJS" plugin (SortableJS).
I use the plugin without react, the basic one in a table to move the rows.
Thanks in advance ! :D
For the ones that fall here from G like me, something like this will do it:
var grid = document.getElementById('grid');
new Sortable(grid, {
animation: 150,
swap: true,
swapThreshold: 0.65,
ghostClass: 'dragFrom',
swapClass: 'dragTo',
forceFallback: true, // This is it!
onChoose: function(e) {
e.target.classList.add('grabbing');
},
onUnchoose: function(e) {
e.target.classList.remove('grabbing');
},
onStart: function(e) {
e.target.classList.add('grabbing');
},
onEnd: function(e) {
e.target.classList.remove('grabbing');
},
onMove: function(e) {
e.target.classList.add('grabbing');
},
});
.grabbing * {
cursor: grabbing !important;
}
.grid {
margin:0;
padding:0;
}
.grid-square {
margin:5px;
display: inline-block;
background-color: #fff;
border: solid 1px rgb(0,0,0,0.2);
text-align: center;
cursor: grab;
padding-top:35px;
width:75px;
height:50px;
}
.grid-square:active {
cursor: grabbing!important;/* fighting on all fronts */
}
.dragFrom{
background-color: #48b4e6!important;
}
.dragTo {
background-color: #30ec5f!important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
<div id="grid">
<div class="grid-square">1</div>
<div class="grid-square">2</div>
<div class="grid-square">3</div>
<div class="grid-square">4</div>
<div class="grid-square">5</div>
<div class="grid-square">6</div>
</div>
The forceFallback: true, line is the key to bypass the issue pointed by Arthur in his comment to the question post.
The onChoose, onUnchoose, onStart, onEnd and most CSS are my dirty solution after testing some times. Hope this helps someone.
I am trying to change the cursor of the draggable item in chrome. Everything i tried it is not working. There are solution on Stackoverflow but they are all outdated and not working with the actual chrome version.
On drag the item is copied to a container which is the dragimage for the draggable.
What i want is to have a grabbing cursor while dragging. How would that be possible? Any Ideas?
See my code snippet for an example.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area',
drag_elements: [
{text: 'one', selected: true},
{text: 'two', selected: false},
{text: 'three', selected: false},
{text: 'four', selected: false},
]
},
computed: {
selected_elements(){
let selected = [];
this.drag_elements.map((drag) => {
if(drag.selected){
selected.push(drag);
}
})
return selected;
}
},
methods: {
drag_it(event){
let html = document.getElementById("dragElement");
let drop_docs = this.selected_elements;
if(drop_docs.length > 1){
let multiple = document.createElement('div');
multiple.classList.add('dragMultiple');
multiple.innerHTML = drop_docs.length + ' items';
html.innerHTML = '';
html.appendChild(multiple)
}else{
html.innerHTML = event.target.outerHTML;
}
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.setDragImage(html, 0, 0);
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
},
select(event, drag_element){
if(event.metaKey || event.shiftKey){
drag_element.selected = !drag_element.selected;
} else {
this.drag_elements.map((drag) => {
if(drag === drag_element){
drag.selected = true;
}else{
drag.selected = false;
}
})
}
}
}
})
#Dragme{
width: 200px;
height: 50px;
margin-left: 20px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 500px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
.selected{
border: 2px solid yellow !important;
}
.dragMultiple{
border: 1px solid black;
padding: 10px;
background-color: white;
}
#dragElement{
position: absolute;
top: 400px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div id="drag_elements">
<div v-for="drag in drag_elements"
#dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
#mouseup="select($event, drag)"
draggable="true"
:class="{selected: drag.selected}"
id="Dragme">{{drag.text}}</div>
</div>
</div>
<div id="dragElement">
</div>
Update
Actually it can be solved with the following answer
CSS for grabbing cursors (drag & drop)
It is important to add the dndclass
thx
Blockquote
#Carr for the hint
Update
After Dragend or drop the cursor is not set to default. Only when moved it changes back. Any Ideas?
Update
With they command key on mac or the shift key multiple items can be selected and dragged. A new dragitem is created for that purpose but the cursor does not allways fall back after dragend or drop.
Update
Integrate method to from answer -by Carr
In fact, setDragImage api is to set the image for replacing that plain document icon which be aside with default cursor, not cursor itself. So your code about '.dragElement' is not working as you expected, it's unstable and causes weird effect when I am testing, I have removed them in my answer.
What I've done below is a little bit tricky, but I think it's at least in correct logic. However, maybe there is a more elegant solution.
new Vue({
el: '#app',
data: {
text_drop: 'Droppable Area',
text_drag: 'Drag Area'
},
methods: {
drag_it(event){
event.dataTransfer.setData('text/plain', '' );
event.dataTransfer.effectAllowed = "move";
},
drag_over(event){
document.documentElement.style.cursor="-webkit-grabbing";
},
drag_end(event){
document.documentElement.style.cursor="default";
}
}
})
#Dragme{
width: 200px;
height: 50px;
text-align: center;
border:1px solid black;
float:left;
}
#Dragme:hover {
cursor: -webkit-grab;
}
#Dragme:active {
cursor: -webkit-grabbing;
}
#Dropzone{
float: left;
width: 300px;
height: 100px;
border: 1px solid;
margin-bottom: 50px;
}
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<div id="Dropzone">{{text_drop}}</div>
<div #dragstart="drag_it"
#dragover="drag_over"
#dragend="drag_end"
draggable="true"
id="Dragme">{{text_drag}}</div>
</div>
Update - derivative problems about original question
"dragImage" sticks at bottom, all elements are disappeared, or flashing sometimes.
And here is still a weird part, id attribute should be unique:
And add quote from MDN document about setDragImage, I wrongly recalled svg in comment, it should be canvas :
... The image will typically be an <image> element but it can also be a
<canvas> or any other image element. ...
We could draw text in canvas, it's another question.
I am working with a Mapbox GL map, similar to something like Leaflet or OpenLayers. For this case, I have allowed a click on the map to activate a popup with text from an attribute of that element of the map. I want this click to also update a variable, img_start, which contains a key for an image in street level view. I have the following code inside my <script></script> inside the <body>. The map element is inside the <div id="map"> and the variable img_start is inside <div id="mly">. So I want a click on the map to update the img_start variable, then refresh the mly div. How can I achieve this, and what am I doing wrong?
Specific code I'm referring to:
map.on('click', function (e) {
//change img_start variable, right now just a test to see if the new value will load
img_start = 'lGQKs30MWMrNJnTjDz-2Ig';
//refresh the 'mly' div so the new start image appears
$("#mly").load(location.href + " #mly");
var features = map.queryRenderedFeatures(e.point, { layers: ['parcels-layer'] });
if (!features.length) {
return;
};
var feature = features[0];
var popup = new mapboxgl.Popup()
.setLngLat(map.unproject(e.point))
//displays the TaxIDNum of the parcel
.setHTML(feature.properties.TaxIDNum)
.addTo(map);
});
// Indicate that the symbols are clickable by changing the cursor style
map.on('mousemove', function (e) {
var features = map.queryRenderedFeatures(e.point, { layers: ['parcels-layer'] });
map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
});
Full code of my HTML document, with the image viewer at the bottom:
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<title>Kelleys Island Parcels Demo</title>
<meta content='initial-scale=1,maximum-scale=1,user-scalable=no' name=
'viewport'>
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.24.0/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.24.0/mapbox-gl.css' rel='stylesheet' />
<script src="jquery-1.12.4.min.js"></script>
<link href=
"https://npmcdn.com/mapillary-js#1.4.1/dist/mapillary-js.min.css" rel=
"stylesheet">
<style>
body {
width: 1350px;
height: 480px;
background-color: black;
}
a:link{
color: ##e60000;
text-decoration: none
}
a:visited{
color: ##e60000;
text-decoration: none
}
a:hover{
color: ##e60000;
text-decoration: none
}
a:active{
color: orange;
text-decoration: none
}
.title {
color: white;
font-family: "Century Gothic";
font-size: 24px;
text-align: center;
font-weight: bolder;
}
.intro {
color: white;
font-family: "Century Gothic";
font-size: 14px;
text-align: center;
}
.mly-wrapper {
position: relative;
background-color: grey;
width: 100%;
height: 100%;
}
.mapillary-js {
position: relative;
height: 100%;
width: 50%;
}
#map {
position: absolute;
width: 50%;
top: 0;
right: 0;
bottom: 0;
z-index: 100;
}
.mapboxgl-popup {
max-width: 400px;
font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;
}
</style>
</head>
<body>
<div class="title">
Kelleys Island Parcels
</div>
<div class="intro">
<br>
Placeholder info
</div><br>
<div class="mly-wrapper">
<div id='mly'></div>
<div id='map'></div>
</div><button onclick="play()">Play</button><button onclick=
"stop()">Stop</button>
<script src=
"https://npmcdn.com/mapillary-js#1.4.1/dist/mapillary-js.min.js">
</script>
<script>
window.img_start = 'KXPQhn74azgtjJYcrGK-Fw';
mapboxgl.accessToken = 'pk.eyJ1IjoiY2JlZGRvdyIsImEiOiI5Q09YRG1RIn0.Izu6OPJ4CEEaSSpGuys3Xg';
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v9',
center: [-82.69965,41.60116],
zoom: 12
});
map.on('load', function () {
map.addSource('parcels', {
'type': 'geojson',
'data': 'https://gist.githubusercontent.com/cbeddow/0bb1a957326cab0aec649e0dea3b978d/raw/f1069a486982efd0898cb915a7f39284fdd43321/kelleys_island.geojson'
});
// Add a layer showing the parcel polygons.
map.addLayer({
'id': 'parcels-layer',
'type': 'fill',
'source': 'parcels',
'source-layer': 'parcels-layer',
'paint': {
'fill-color': 'rgba(200, 100, 240, 0.4)',
'fill-outline-color': 'rgba(200, 100, 240, 1)'
}
});
});
// When a click event occurs on a polygon, open a popup at the location of
// the feature, with description HTML from its properties.
map.on('click', function (e) {
//change img_start variable, right now just a test to see if the new value will load
img_start = 'lGQKs30MWMrNJnTjDz-2Ig';
//refresh the 'mly' div so the new start image appears
$("#mly").load(location.href + " #mly");
var features = map.queryRenderedFeatures(e.point, { layers: ['parcels-layer'] });
if (!features.length) {
return;
};
var feature = features[0];
var popup = new mapboxgl.Popup()
.setLngLat(map.unproject(e.point))
//displays the TaxIDNum of the parcel
.setHTML(feature.properties.TaxIDNum)
.addTo(map);
});
// Indicate that the symbols are clickable by changing the cursor style
map.on('mousemove', function (e) {
var features = map.queryRenderedFeatures(e.point, { layers: ['parcels-layer'] });
map.getCanvas().style.cursor = (features.length) ? 'pointer' : '';
});
var mly = new Mapillary.Viewer('mly',
'UTZhSnNFdGpxSEFFREUwb01GYzlXZzpkMWM2YzdjYjQxN2FhM2Vh', // ClientID
img_start, {cover: true, cache: false, direction: false});
</script>
</body>
</html>
Here I have been able to drop elements onto a canvas and create connections between them. But every time I drag a dropped element within the canvas, the anchors do not move along with the dragged element. Instead when I try to create a connection from the isolated anchor to another element it immediately re-positions itself with its parent element. This is one issue and I would also like to delete the anchors/ connections whenever its parent element is deleted.
<!doctype html>
<html>
<head>
<script src="../lib/jquery.min.js"></script>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="../lib/jquery-ui.min.js"></script>
<script src="../lib/jquery.jsPlumb-1.6.4-min.js"></script>
<style>
.chevron-toolbox{
position: absolute;
width: 72px;
height: 80px;
background-color: powderblue;
background-image: url("../dist/img/bigdot.png");
border: solid 3px red;
}
#dropArea{
cursor: pointer;
border: solid 1px gray;
width: 800px;
margin-left: 80px;
height: 400px;
position: relative;
overflow-x: scroll;
overflow-y: scroll;
}
.chevron {
position:absolute;
cursor:pointer;
width: 72px;
height: 80px;
background-color: powderblue;
background-image: url("../dist/img/bigdot.png");
}
</style>
</head>
<body>
<div class="chevron-toolbox" id="cId">
</div>
<div id="dropArea">
</div>
<button id="go">Double Click Me</button>
<script>
jsPlumb.ready(function(e)
{
jsPlumb.setContainer($('#dropArea'));
$(".chevron-toolbox").draggable
({
helper : 'clone',
cursor : 'pointer',
tolerance : 'fit',
revert : true
});
$("#dropArea").droppable
({
accept : '.chevron-toolbox',
containment : 'dropArea',
drop : function (e, ui) {
droppedElement = ui.helper.clone();
ui.helper.remove();
$(droppedElement).removeAttr("class");
jsPlumb.repaint(ui.helper);
$(droppedElement).addClass("chevron");
$(droppedElement).draggable({containment: "dropArea"});
$(droppedElement).appendTo('#dropArea');
setId(droppedElement);
var droppedId = $(droppedElement).attr('id');
var common = {
isSource:true,
isTarget:true,
connector: ["Flowchart"],
};
jsPlumb.addEndpoint(droppedId, {
anchors:["Right"]
}, common);
jsPlumb.addEndpoint(droppedId, {
anchors:["Left"]
}, common);
alert(droppedId);
//Delete an element on double click
var dataToPass = {msg: "Confirm deletion of Item"};
$(droppedElement).dblclick(dataToPass, function(event) {
alert(event.data.msg);
$(this).remove();
});
}
});
//Set a unique ID for each dropped Element
var indexer = 0;
function setId(element){
indexer++;
element.attr("id",indexer);
}
});
</script>
</body>
</html>
In order to properly manipulate the connections, you can use the connect method in jsPlumb placing anchors at desired points.
jsPlumb.connect({
source:'window2',
target:'window3',
paintStyle:{lineWidth:8, strokeStyle:'rgb(189,11,11 )'},
anchors:["Bottom", "Top"],
endpoint:"Rectangle"
});
This is merely an example. Following this pattern in your implementation will be useful when it comes to accessing details regarding those connections and deleting the connections alongside the elements
I'm trying to create a flow digram using data from the server, I am able to draw the states and connection correctly, states are draggable but same is not working with the connectors.
please see the sample code below.
<html>
<head>
<script src="../../lib/jquery-1.9.0.js"></script>
<script src="../../lib/jquery-ui-1.9.2-min.js"></script>
<script src="../../lib/jquery.jsPlumb-1.4.1-all.js"></script>
<script>
$(document).ready(function() {
var i = 0;
var top = 50;
var left = 500;
for (var j = 0; j <= 5; j++) {
top += 150;
var newState = $('<div>').attr('id', 'state' + j).addClass('item');
var title = $('<div>').addClass('title').text('State ' + j);
newState.css({
'top': top,
'left': left
});
newState.append(title);
$('#container').append(newState);
if (j > 0) {
var firstInstance = jsPlumb.getInstance();
firstInstance.importDefaults({
Connector: ["Flowchart", {curviness: 150}],
Anchors: ["BottomCenter", "TopCenter"]
});
firstInstance.connect({
endpoint: "Rectangle",
source: "state" + (j-1),
target: "state" + (j),
paintStyle: {lineWidth: 3, strokeStyle: 'black'},
overlays: [
"Arrow",
["Label", {location: 0.25, id: "myLabel"}]
]
});
}
jsPlumb.draggable(newState, {
containment: 'parent'
});
}
});
</script>
<style type="text/css">
.item {
border: 1px solid black;
background-color: #ddddff;
}
#container {
border: 1px solid gray;
width: 1500px;
height: 1500px;
}
.title {
padding: 10px;
cursor: move;
}
.item {
position: absolute;
border: 1px solid black;
background-color: #ddddff;
}
</style>
<title>Getting started with jsPlumb</title>
</head>
<body>
<div id="container"></div>
</body>
I need to make the connectors drag-able, any help is apprenticed.
issue is solved
used jsPlumb.connect() instesd of firstInstance.connect().