When modifying a feature, there is the option to delete a single vertex. According to the docs it says:
removePoint(){boolean}:
Removes the vertex currently being pointed.
(https://openlayers.org/en/latest/apidoc/ol.interaction.Modify.html)
Since I work on mobile devices, I would like to present a popup with a delete button next to the a vertex if a user clicks or hovers over it. Therefore I need the coordinates of this vertex. I can see the vertex indicated on the map with a different style, but how can I get it or its coordinates?
It must be somewhere in some selection because the automatic "pointing to" style and removePoint method works per se fine.
Here is a solution that use a button to delete a vertex.
The button is shown or hidden if there is a vertex to delete (it could be a popup).
It uses:
condition option to show the button if there is a point near the click
insertVertexCondition option to hide the button (there is no vertex here)
modifystart and modifyend event to hide the button (we are moving and we don't want the button to be visible)
removePoint function when click on the button
The button is not show if you're moving or if there is no point to delete.
It doesn't rely on features that are not documented or private.
You can see it in action here.
var btElt = document.getElementById("delete");
// Modify interaction
var mod = new ol.interaction.Modify({
source: vector.getSource(),
// Show on select
condition: function(e){
// Check if there is a feature to select
var f = this.getMap().getFeaturesAtPixel(e.pixel,{
hitTolerance:5
});
if (f) {
var p0 = e.pixel;
var p1 = f[0].getGeometry().getClosestPoint(e.coordinate);
p1 = this.getMap().getPixelFromCoordinate(p1);
var dx = p0[0]-p1[0];
var dy = p0[1]-p1[1];
if (Math.sqrt(dx*dx+dy*dy) > 8) {
f = null;
}
}
if (f) btElt.style.display = "inline-block";
else btElt.style.display = "none";
return true;
},
// Hide on insert
insertVertexCondition: function(e) {
btElt.style.display = "none";
return true;
}
});
// Hide on modifying
mod.on(['modifystart','modifyend'], function(){
btElt.style.display = "none";
});
map.addInteraction (mod);
// Delete vertex when click the button
function deleteVertex() {
mod.removePoint();
btElt.style.display = "none";
}
Alright digging into the source code I came up with a kinda ugly but so far working solution. For the record, this is the code.
this.modify = new Modify({
features: new Collection([this.selectedFeature]),
pixelTolerance:30,
condition: (event)=>{
if(this.modify["lastPointerEvent_"] && this.modify["vertexFeature_"])
this.removePointPopup.setPosition(this.modify["lastPointerEvent_"].coordinate);
else
this.removePointPopup.setPosition(undefined);
return true;
}
});
this.modify.on("modifyend",ev=>{
this.removePointPopup.setPosition(ev.target["lastPointerEvent_"].coordinate);
})
[...]
removePoint(){
this.modify.removePoint()
this.removePointPopup.setPosition(undefined);
}
If someone knows about a nicer solution, feel free to post it.
Related
I am trying to create a simple drag and drop activity in Adobe CC.
I have got the dragging function working fine, however I can't get it to detect when the dragged button hits the target.
Any help would be greatly appreciated!
Here is my code:
function Main()
{
createjs.Touch.enable(stage);//Emable Touch Gesture Recognition
createjs.Ticker.addEventListener("tick", this.update.bind(this));
this.addTargetsToButtons();//Add Button Actions
}
var phonemes = ["s","a","t","p","i","n","m","d","g","o","c","k","ck","e","u","r","h","b","f","ff","l","ll","ss"];
var draggablePhonemes = [this.b0, this.b1, this.b2];
var targets = [this.target1];
Main.prototype.addTargetsToButtons= function(){//Add Actions To All Our Buttons
for (i = 0; i<draggablePhonemes.length; i++){
console.log(draggablePhonemes[i]);
draggablePhonemes[i].addEventListener("pressmove", draggedStart.bind(this));
draggablePhonemes[i].addEventListener("pressup",draggedStop.bind(this));
draggablePhonemes[i].name = phonemes[i];
}
}
function draggedStart(event) {
console.log(event.currentTarget.name)
var p = stage.globalToLocal(event.stageX, event.stageY);
event.currentTarget.x = p.x;
event.currentTarget.y = p.y;
//The buttons can be dragged onto target1, target2, target3
}
function draggedStop(event) {
console.log(event.currentTarget.name + " Has Stopped Moving")
if (event.currentTarget.hitTest(targets[0].x,targets[0].y)){
console.log("Collision");
}
}
You need to add ondragover data attribute to the target and set it to this function:
function allowDrop(event) {
event.preventDefault();
console.log("Look, it hits the target");
}
By default, data/elements cannot be dropped in other elements. To allow that to happen, you must prevent the default handling of the element.
Note: You might also need to add ondrop data attribute to the target set to a function, so the actual drop can happen.
ex.
function drop(event) {
event.preventDefault();
var data = event.currentTarget.name;
event.target.appendChild(document.getElementById(data));
}
And this is what the html element should look like:
<div ondrop="drop(event)" ondragover="allowDrop(event)"></div>
I have individual three arrows. on click; I want the div below them (letsChat) to change styles and I want to clone and append relevant information in that div. I also want it to revert back to it's original state when it is clicked again or if orientation is changed to portrait.
document.querySelector('#compositionArrow').onclick = function(){
var letsChat = document.querySelector('.lets-chat');
var letsChatButton = document.querySelector('.lets-chat a');
var compositionArrow = document.querySelector('#compositionArrow')
var compositionText = document.querySelector('.composition-text');
if (letsChatButton.style.display='flex' && window.matchMedia("(orientation: landscape)").matches) {
compositionArrow.style.transform='rotate(180deg)';
//letsChat.appendChild(compositionText).cloneNode(true);
//compositionText.clone().appendTo.letsChat; return false;
document.querySelector('.composition-text').clone().appendTo(document.querySelector('.lets-chat'));
letsChat.style.background='#00BCD4';
letsChatButton.style.display='none';
}
else if (letsChatButton.style.display='none' || window.matchMedia("(orientation: portrait)").matches){
compositionArrow.style.transform='rotate(180deg)';
letsChat.style.background='#ff8f00';
letsChatButton.style.display='flex';
}
}
example can be found below: (you may have to play with window
artpenleystudios.com
Here's something that demonstrates part of what you asked. It doesn't take into account orientation change, but it handles the click part. As far as I know, there's no straightforward way to detect orientation change, but here's an article that talks about a few options. Another idea would be to use jQuery Mobile as it fires orientationchange events.
So after much back and forth and after looking at your site more closely, this is what I managed to cobble together.
jQuery('#compositionArrow').click(function() {
var $letsChat = jQuery('.lets-chat');
var letsChat = $letsChat[0];
var letsChatButton = $letsChat.find('a')[0];
// Remove old composition text if it exists.
$letsChat.find('.composition-text').remove();
if (letsChatButton.style.display !== 'none' && window.matchMedia("(orientation: landscape)").matches) {
this.style.transform = 'rotate(180deg)';
$letsChat.append(jQuery('.composition-text').clone());
letsChat.style.background = '#00BCD4';
letsChatButton.style.display = 'none';
}
else if (letsChatButton.style.display === 'none' || window.matchMedia("(orientation: portrait)").matches) {
this.style.transform = '';
letsChat.style.background = '#ff8f00';
letsChatButton.style.display = 'flex';
}
});
It works for me in FireFox on a downloaded version of your site.
Cheers!
Alright, I have a chess board with all the spots and pieces. It's generated onload with two four cycles from a 2D array. The idea is, that you should be able to drag a chess piece (something I've already implemented correctly), and let it go in a valid position. The letting go part should trigger an event in the actual board spots. So each of the 64 spots have an even listener that looks like this.
element.on("mouseup",function(e){
if(Game.current==undefined||!$(this).hasClass("valid")){return;}
var col,row;
col = $(this).attr("column");
row = $(this).attr("row");
Game.current.attr({
"column":col,
"row":row
});
Game.move(Game.current);
Game.changeTurn();
Game.current = undefined;
});
The function triggers normally when I click on a square in the board, however when I drag and drop a chess piece there, it doesn't register that the mouse is up. It let's go of the piece, however since the cursor is directly over the chess piece and said piece "blocks" the cursor from being directly above the spot. It is also worth noting that my HTML structure is like this.
<board>
<spot></spot>
<piece></piece>
</board>
By which I mean, that the pawns, bishop etc. aren't inside the spots, so the event doesn't have anything to "bubble" through.
A better approach would be attach the listener of mouseup to the window object doing something like this
$(window).on('mouseup', on_mouse_up );
and then, from the mouse position calculating the right spot.
function on_mouse_up( e ) {
// Get the mouse position
x = e.pageX;
y = e.pageY;
// Calculate the spot from the mouse position
}
(function ($) {
"use strict";
//console.log($);
$('document').ready(function () {
var target;
$('#wrapper').mousedown(function (e) {
var el = $(e.target);
if (el.hasClass('elem')) {
target = el;
}
});
$('#wrapper').mouseup(function (e) {
var el = $(e.target);
//console.log(e);
if (target) {
if (el.hasClass('kletca')) {
el.append(target);
}
}
target = undefined;
});
});
}(jQuery));
http://jsfiddle.net/32DTv/ a simple example
This is for homework and it has an annoying bug I can't solve after hours of struggle.
http://canvaseu.chrisloughnane.net/
When on the eu map clicking a country path fires the bound event.
This event destroys the layers children and loads the country. BUT if you click and quickly move the mouse and let go the button the country loads but the country path from the eu remains. This is best demonstrated with Spain as shown in the screen grab.
I am hoping a callback after mapLayer.destroyChildren(); to then call the load function would solve my problem.
This can be a little difficult to replicate.
I'm sure my control is too tied up with my view but I haven't been able to see a solution to separate them neatly.
**** EDIT ****
I came up with a solution that works partially but I think this is terrible hack code, I added to the mousedown binding down = true; and added checks to the mouseout binding, please see below.
What I think is happening is when you move the mouse and let the button go very quickly the mouseover binding is over riding the mouseup.
This solution isn't ideal, after loading a number of countries and mouseover on regions the canvas response slows down.
Event Binding
path.on('mousedown touchstart', function()
{
down = true;
this.setStroke('red');
this.moveTo(topLayer);
/****
* Handle if the path we are displaying in canvas is the eu
* to allow selection and load of country path point data.
*/
if (associativeCountryArray[lastCountry].getText() == 'eu')
{
associativeCountryArray[lastCountry].setFill('#bbb');
associativeCountryArray[lastCountry].setFontStyle('normal');
countrynames[lastCountry].selected = false;
this.moveTo(mapLayer);
mapLayer.destroyChildren();
lastCountry = this.getName();
countrynames[this.getName()].selected = true;
associativeCountryArray[this.getName()].setFill("rgb(" + r + "," + g + "," + b + ")");
associativeCountryArray[this.getName()].setFontStyle('bold');
loadPaths(this.getName().replace(/\s/g, ''));
countryNameLayer.draw();
}
else
{
window.open('https://www.google.com/search?q=' + this.getName(),'_blank');
}
topLayer.drawScene();
});
path.on('mouseout', function()
{
if(!down)
{
document.body.style.cursor = 'default';
this.setFill('#eee');
this.setStrokeWidth(settings.strokewidthstart);
/****
* On hover, only change colour of country names around edge of canvas if we are on the 'eu' map
*/
if (lastCountry == 'eu')
{
associativeCountryArray[this.getName()].setFill('#bbb');
associativeCountryArray[this.getName()].setFontStyle('normal');
}
this.setStroke('#555');
this.moveTo(mapLayer);
writeMessage('');
topLayer.draw();
countryNameLayer.draw();
}
else
{
down = false;
}
});
path.on('mouseup touchend', function()
{
this.setStroke('black');
topLayer.drawScene();
down = false;
});
Like this:
Send the container (group,layer) you want to clear and the callback you want triggered.
function myDestroyChildren(container,callback) {
var children = container.getChildren();
while(children.length > 0) {
children[0].destroy();
}
callback();
}
I'm trying to drag and drop items on a page, but insteads of moving the item itself, I create a copy of it.
Here is my code. "copyDragDrop" is a div at the bottom of the page. It remains empty until the user strats dragging something.
function coordSouris()
{
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function drag()
{
var pos = coordSouris(event);
copie = event.srcElement.cloneNode(true);
document.getElementById('copieDragDrop').appendChild(copie);
copie.style.position = 'absolute';
copie.style.display = 'block';
document.onmousemove = mouseMove;
document.onmouseup = drop;
}
function mouseMove()
{
if (copie != null)
{
var pos = coordSouris(event);
copie.style.left = pos.x;
copie.style.top = pos.y;
}
}
function drop()
{
var divCopie = document.getElementById('copieDragDrop');
if (divCopie.hasChildNodes() )
{
while ( divCopie.childNodes.length >= 1 )
{
divCopie.removeChild(divCopie.firstChild);
}
}
}
This code creates the copy, starts to move it, but after a few pixels, the copy stops following the mouse. If I release the mouse, "onmouseup" is not fired, but the copy starts to follow the mouse again ! I've tried the code on several items, the same bug occurs
I don't understand anything, any help is more than welcome.
UPDATE : I juste realised that all elements I tried the code on had something in common : they contained or were included in an ASP.net hyperlink control. The same code works well on regular HTML elements. There must be some auto-generated javascript for links that interferes with my code.
Couldn't find the auto-generated code responsible for the issue, so I simply solvec this by replacing Hyperlink controls by standard HTML anchors.