I'm beginning to use canvas and kineticjs. I tried to draw a rect when clicking the button "add text", in the function I declare a var Text, add the var Text in the layer and then add the layer to the stage, can anybody help me?
window.onload = function() {
var stage = new Kinetic.Stage({
container: "container",
width: 578,
height: 200
});
var shapesLayer = new Kinetic.Layer();
document.getElementById("Draw_text").addEventListener("click", function () {
var Text = new Kinetic.Text({
x: 100,
y: 100,
text: "hi",
draggable: "true"
});
shapesLayer.add(Text);
stage.add(shapesLayer);
}, false);
<body onmousedown="return false;">
<div id="container"></div>
<div id="buttons">
<button id="Draw_text">
add text
</button>
</div>
You have to change your strategy.
In order to do this you can use a very simple trick. First draw any shape that you need and then use hide() and show() function. By using this method u can get what you are looking for.
* Do Not declare your text inside the onclick function.
window.onload = function() {
var stage = new Kinetic.Stage({
container: "container",
width: 578,
height: 200
});
var shapesLayer = new Kinetic.Layer();
var Text = new Kinetic.Text({
x: 100,
y: 100,
text: "hi",
draggable: "true"
});
document.getElementById("Draw_text").addEventListener("click", function () {
Text.show();
shapesLayer.draw();
}, false);
document.getElementById("Delete_text").addEventListener("click", function () {
Text.hide();
shapesLayer.draw();
}, false);
shapesLayer.add(Text);
stage.add(shapesLayer);
};
Related
I'm using OpenLayers V4 and I'm trying to see if it's possible to allow a user to click on a feature's vector label and move/drag it to a location of their choice. My initial thought was to capture when a user clicked on the label, and then dynamically calculate and set the offsetX and offsetY properties of the label (ol.style.Text) as the user's mouse pointer moved around. To achieve this, I need to capture when the user clicks on the label and not the feature itself. The main problem is that I can't find a way to distinguish this. It appears as though the label is part of the vector feature because clicking on the feature highlights both the feature and the label and vice versa.
In summary, my question is two fold:
Does anyone have an idea how to create a user draggable vector label in OpenLayers 4?
Is there a way to detect/distinguish between a user clicking on a vector feature itself, or the vector label.
Note: I'm familiar with overlays and realize they might be easier to work with since they have a setPosition property, but the way my web map is constructed I need to display vector labels for each feature and not overlays
It is possible using vector labels in OpenLayers 6 where the modify interaction has access to the features being modified in its style function and can use hit detection of offset labels, but that is not available in version 4. In this example labels move with their features, but can also be moved independently of the features. Clones are needed to avoid changing feature geometries while moving the labels. The labels geometries are then restored and replaced with offsets for styling. When a feature is moved its label clone is kept in sync.
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io#master/en/v6.5.0/build/ol.js"></script>
<style>
html, body, .map {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script>
var white = [255, 255, 255, 1];
var blue = [0, 153, 255, 1];
var width = 3;
var modifyStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: width * 2,
fill: new ol.style.Fill({
color: blue
}),
stroke: new ol.style.Stroke({
color: white,
width: width / 2
})
}),
zIndex: Infinity
});
var labelStyle = new ol.style.Style({
text: new ol.style.Text({
offsetY: 10,
font: '12px Calibri,sans-serif',
fill: new ol.style.Fill({
color: '#000',
}),
stroke: new ol.style.Stroke({
color: '#fff',
width: 3,
}),
backgroundFill: new ol.style.Fill({
color: 'rgba(0 ,0, 0, 0)',
}),
}),
});
var featureLayer = new ol.layer.Vector({
source: new ol.source.Vector({
url: 'https://mikenunn.net/data/world_cities.geojson',
format: new ol.format.GeoJSON(),
}),
});
var labelLayer = new ol.layer.Vector({
source: new ol.source.Vector(),
renderBuffer: 1e3,
style: function (feature) {
labelStyle.getText().setOffsetX(feature.get('offsetX') || 0);
labelStyle.getText().setOffsetY((feature.get('offsetY') || 0) - 10);
labelStyle.getText().setText(feature.get('CITY_NAME'));
return labelStyle;
},
});
featureLayer.getSource().on('addfeature', function(event) {
var id = event.feature.getId();
var feature = event.feature.clone();
feature.setId(id);
labelLayer.getSource().addFeature(feature);
});
featureLayer.getSource().on('removefeature', function(event) {
var id = event.feature.getId();
var source = labelLayer.getSource();
source.removeFeature(source.getFeatureById(id));
});
var defaultStyle = new ol.interaction.Modify({
source: featureLayer.getSource()
}).getOverlay().getStyleFunction();
var featureModify = new ol.interaction.Modify({
source: featureLayer.getSource(),
style: function(feature) {
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
var geometry = feature.getGeometry().clone();
labelLayer.getSource().getFeatureById(id).setGeometry(geometry);
});
return defaultStyle(feature);
}
});
var labelModify = new ol.interaction.Modify({
source: labelLayer.getSource(),
hitDetection: labelLayer,
style: function(feature) {
var styleFeature;
feature.get('features').forEach( function(modifyFeature) {
var id = modifyFeature.getId();
styleGeometry = featureLayer.getSource().getFeatureById(id).getGeometry();
});
modifyStyle.setGeometry(styleGeometry);
return modifyStyle;
}
});
labelModify.on('modifyend', function(event) {
event.features.forEach( function(feature) {
var id = feature.getId();
var labelCoordinates = feature.getGeometry().getCoordinates();
var geometry = featureLayer.getSource().getFeatureById(id).getGeometry().clone();
var featureCoordinates = geometry.getCoordinates();
var resolution = map.getView().getResolution();
var offsetX = (labelCoordinates[0] - featureCoordinates[0]) / resolution + (feature.get('offsetX') || 0);
var offsetY = (featureCoordinates[1] - labelCoordinates[1]) / resolution + (feature.get('offsetY') || 0);
feature.set('offsetX', offsetX, true);
feature.set('offsetY', offsetY, true);
feature.setGeometry(geometry);
});
});
var map = new ol.Map({
layers: [featureLayer, labelLayer],
interactions: ol.interaction.defaults().extend([labelModify, featureModify]),
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([5, 51]),
zoom: 8
})
});
</script>
</body>
</html>
I known this question has been raised and answered many times but I can't seem to make the suggested solutions work for me...
I'm displaying a google map within a simplemodal dialogue.
Outside the modal dialogue the map displays correctly.
However, once inside a modal wrapper only part of the map is shown on the first iteration (see below).
The solution would appear to involve binding a 'resize' event to the map but it isn't working for me...
First iteration:
On opening the modal for the first iteration, my map displays with the partial section displaced over to the top RHS and overlaid on the full map.
On closing the first dialogue the screen returns to it's initial state.
Second and subsequent iterations:
On subsequent iterations the map displays correctly but on closing the background color of the map canvas is visible.
HTML:
<body>
<button class="modalMap">With Modal </button>
<button class="nonModalMap">Without Modal </button>
<div id="mapCanvas"></div>
</body>
CSS:
#simplemodal-overlay {background-color:#000;}
#simplemodal-container {color:#999; background-color:#fff;}
#simplemodal-container a{color:#ddd;}
.modalMap:hover,.nonModalMap:hover {
cursor:pointer;
}
#mapCanvas {
position:relative;
width:480px;height:300px;
}
JS:
$(document).ready(function(){
var myMap;
$('.modalMap').click(function(){
buildMap();
$('#mapCanvas').modal(
{onOpen:function(dialog){
dialog.overlay.fadeIn('fast',function(){
dialog.data.hide();dialog.container.fadeIn('fast',function(){
dialog.data.slideDown('fast');
});
});
}
,onClose:function(dialog){
dialog.data.fadeOut('fast',function(){
dialog.container.hide('fast',function(){
dialog.overlay.slideUp('fast',function(){
$.modal.close();
});
});
});
}
});
});
/* But without the modal the map displays correctly... */
$('.nonModalMap').click(function(){
buildMap();
});
});
function buildMap() {
var kpl = {
Place: function (data, map) {
var self = this;
this.data = data;
var coords = data.geo_coords.split(',');
this.position = new google.maps.LatLng(coords[0], coords[1]);
this.marker = new google.maps.Marker({
position: this.position,
map: map
});
google.maps.event.addListener(this.marker, 'click', function() {
if (self.data.url) {
window.location.href = self.data.url
}
});
},
MapManager: function (div, data) {
this.map = new google.maps.Map(div, {
zoom: 15,
center: new google.maps.LatLng(53.818298, -1.573263),
mapTypeId: google.maps.MapTypeId.ROADMAP,
scrollwheel: false,
backgroundColor: '#cccccc',
streetViewControl: false,
navigationControl: true,
mapTypeControlOptions: {style: google.maps.MapTypeControlStyle.DROPDOWN_MENU}
});
saveCenter = this.map.center;
this.places = [];
for (var i in data) {
if (data.hasOwnProperty(i)) {
this.places.push(new kpl.Place(data[i], this.map));
}
}
}
};
myMap = new kpl.MapManager($('#mapCanvas').get(0), [{
url: "mailto:info#????.com",
geo_coords: "53.818298, -1.573263",
name: "Kensington Property LS6"
}]);
}
/* plus the simplemodal library... */
I've recreated the code in jsfiddle - http://jsfiddle.net/redApples/j23ue0n1/32/
Can anybody rescue my sanity...?
I'm using this code to capture the co-ordinates when user clicks on the map by using below event listener:
google.maps.event.addListener(map, 'click', function(event) {
placeMarker(event.latLng);
});
However this function doesn't get called when user click on already marked location in Map.
Meaning this function is not called for points where mouse pointer changes to hand icon on Google Map.
Need help on capturing these kind of locations.
You should add the click listener on marker will give you the position of marker.
//Add listener
google.maps.event.addListener(marker, "click", function (event) {
var latitude = event.latLng.lat();
var longitude = event.latLng.lng();
console.log( latitude + ', ' + longitude );
}); //end addListener
Edit:
You need something like this
//Add listener
google.maps.event.addListener(marker, "click", function (event) {
var latitude = event.latLng.lat();
var longitude = event.latLng.lng();
console.log( latitude + ', ' + longitude );
radius = new google.maps.Circle({map: map,
radius: 100,
center: event.latLng,
fillColor: '#777',
fillOpacity: 0.1,
strokeColor: '#AA0000',
strokeOpacity: 0.8,
strokeWeight: 2,
draggable: true, // Dragable
editable: true // Resizable
});
// Center of map
map.panTo(new google.maps.LatLng(latitude,longitude));
}); //end addListener
Another solution is to place a polygon over the map, same size as the map rectangle, and collect this rectangles clicks.
function initialize() {
var mapDiv = document.getElementById('map-canvas');
var map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
google.maps.event.addListener(map, 'bounds_changed', function() {
var lat1 = 37.41463623043073;
var lat2 = 37.46915383933881;
var lng1 = -122.1848153442383;
var lng2 = -122.09898465576174;
var rectangle = new google.maps.Polygon({
paths : [
new google.maps.LatLng(lat1, lng1),
new google.maps.LatLng(lat2, lng1),
new google.maps.LatLng(lat2, lng2),
new google.maps.LatLng(lat1, lng2)
],
strokeOpacity: 0,
fillOpacity : 0,
map : map
});
google.maps.event.addListener(rectangle, 'click', function(args) {
console.log('latlng', args.latLng);
});
});
}
Now you get LatLng's for places of interest (and their likes) also.
demo -> http://jsfiddle.net/qmhku4dh/
You're talking about the Point of Interest icons that Google puts on the map.
Would it work for you to remove these icons entirely? You can do that with a Styled Map. To see what this would look like, open the Styled Map Wizard and navigate the map to the area you're interested in.
Click Point of interest under Feature type, and then click Labels under Element type. Finally, click Visibility under Stylers and click the Off radio button under that.
This should remove all of the point of interest icons without affecting the rest of the map styling. With those gone, clicks there will respond to your normal map click event listener.
The Map Style box on the right should show:
Feature type: poi
Element type: labels
Visibility: off
If the result looks like what you want, then click Show JSON at the bottom of the Map Style box. The resulting JSON should like this this:
[
{
"featureType": "poi",
"elementType": "labels",
"stylers": [
{ "visibility": "off" }
]
}
]
You can use that JSON (really a JavaScript object literal) using code similar to the examples in the Styled Maps developer's guide. Also see the MapTypeStyle reference for a complete list of map styles.
This example demonstrates the use of click event listeners on POIs (points of interest). It listens for the click event on a POI icon and then uses the placeId from the event data with a directionsService.route request to calculate and display a route to the clicked place. It also uses the placeId to get more details of the place.
Read the google documentation.
<!DOCTYPE html>
<html>
<head>
<title>POI Click Events</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
.title {
font-weight: bold;
}
#infowindow-content {
display: none;
}
#map #infowindow-content {
display: inline;
}
</style>
</head>
<body>
<div id="map"></div>
<div id="infowindow-content">
<img id="place-icon" src="" height="16" width="16">
<span id="place-name" class="title"></span><br>
Place ID <span id="place-id"></span><br>
<span id="place-address"></span>
</div>
<script>
function initMap() {
var origin = {lat: -33.871, lng: 151.197};
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 18,
center: origin
});
var clickHandler = new ClickEventHandler(map, origin);
}
/**
* #constructor
*/
var ClickEventHandler = function(map, origin) {
this.origin = origin;
this.map = map;
this.directionsService = new google.maps.DirectionsService;
this.directionsDisplay = new google.maps.DirectionsRenderer;
this.directionsDisplay.setMap(map);
this.placesService = new google.maps.places.PlacesService(map);
this.infowindow = new google.maps.InfoWindow;
this.infowindowContent = document.getElementById('infowindow-content');
this.infowindow.setContent(this.infowindowContent);
// Listen for clicks on the map.
this.map.addListener('click', this.handleClick.bind(this));
};
ClickEventHandler.prototype.handleClick = function(event) {
console.log('You clicked on: ' + event.latLng);
// If the event has a placeId, use it.
if (event.placeId) {
console.log('You clicked on place:' + event.placeId);
// Calling e.stop() on the event prevents the default info window from
// showing.
// If you call stop here when there is no placeId you will prevent some
// other map click event handlers from receiving the event.
event.stop();
this.calculateAndDisplayRoute(event.placeId);
this.getPlaceInformation(event.placeId);
}
};
ClickEventHandler.prototype.calculateAndDisplayRoute = function(placeId) {
var me = this;
this.directionsService.route({
origin: this.origin,
destination: {placeId: placeId},
travelMode: 'WALKING'
}, function(response, status) {
if (status === 'OK') {
me.directionsDisplay.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
}
});
};
ClickEventHandler.prototype.getPlaceInformation = function(placeId) {
var me = this;
this.placesService.getDetails({placeId: placeId}, function(place, status) {
if (status === 'OK') {
me.infowindow.close();
me.infowindow.setPosition(place.geometry.location);
me.infowindowContent.children['place-icon'].src = place.icon;
me.infowindowContent.children['place-name'].textContent = place.name;
me.infowindowContent.children['place-id'].textContent = place.place_id;
me.infowindowContent.children['place-address'].textContent =
place.formatted_address;
me.infowindow.open(me.map);
}
});
};
</script>
<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap"
async defer></script>
</body>
</html>
If you are using npm load-google-maps-api with webpack this worked for me:
const loadGoogleMapApi = require("load-google-maps-api");
loadGoogleMapApi({ key: process.env.GOOGLE_MAP_API_KEY }).then(map => {
let mapCreated = new map.Map(mapElem, {
center: { lat: lat, lng: long },
zoom: 7
});
mapCreated.addListener('click', function(e) {
console.log(e.latLng.lat()); // this gives you access to the latitude value of the click
console.log(e.latLng.lng()); // gives you access to the latitude value of the click
var marker = new map.Marker({
position: e.latLng,
map: mapCreated
});
mapCreated.panTo(e.latLng); // finally this adds red marker to the map on click.
});
});
Next if you are integrating openweatherMap in your app you can use the value of e.latLng.lat() and e.latLng.lng() which I console logged above in your api request. This way:
http://api.openweathermap.org/data/2.5/weather?lat=${e.latLng.lat()}&lon=${e.latLng.lng()}&APPID=${YOUR_API_KEY}
I hope this helps someone as it helped me.
Cheers!
I need to allow some website previews inside sencha touch. I see two different possibilities:
opening safariMobile-app to show the link
generating a panel with an iFrame inside the 'html'-property.
because I don't know how to achieve 1. I started with 2. but run into some trouble:
a) the content of my iFrameis not scrollable. if I try to scroll the content, the whole viewport scrolls, including my bottom-tabPanel-Buttons!
b) the displayed website seems to load without any css or images
here is my previewPanel-code:
app.views.WebsitePreview = Ext.extend(Ext.Panel, {
layout: 'card',
scroll: 'vertical',
styleHtmlContent: true,
fullscreen: true,
initComponent: function(){
this.html = '<iframe width="100%" height="100%" src="'+ this.theLink + '"></iframe>',
var toolbarBase = {
xtype: 'toolbar',
title: 'Vorschau ' //+ this.childData.childUsername,
};
if (this.prevCard !== undefined) {
toolbarBase.items = [
{
xtype: 'button',
ui: 'back',
text: 'zurück', //this.prevCard.title,
scope: this,
handler: function(){
this.baseScope.setActiveItem(this.prevCard, { type: 'slide', reverse: true });
}
}
]
};
this.dockedItems = toolbarBase;
app.views.WebsitePreview.superclass.initComponent.call(this);
}
});
Ext.reg('websitepreview', app.views.WebsitePreview);
thnx for your help!
I spent two days fighting with the same problem. It seems that finally I found a solution.
The first thing you should try is to use new built-in feature introduced in iOS 5.
-webkit-overflow-scrolling:touch;
You need to wrap your iframe with div, something like:
...
this.html = '<div style="-webkit-overflow-scrolling:touch; height: 500px; overflow: auto;"><iframe .../></div>'
...
If it doesn't work (in my case it worked only first time) then you can try to handle touch events by yourself. Let's say you have the following structure in html:
<div id="wrapper">
<iframe id="my-iframe" .../>
</div>
to make iframe scrollable you need to add this JS
var startY = 0;
var startX = 0;
var ifrDocument = document.getElementById("my-iframe").contentWindow.document;
ifrDocument.addEventListener('touchstart', function (event) {
window.scrollTo(0, 1);
startY = event.targetTouches[0].pageY;
startX = event.targetTouches[0].pageX;
});
ifrDocument.addEventListener('touchmove', function (event) {
event.preventDefault();
var posy = event.targetTouches[0].pageY;
var h = document.getElementById("wrapper");
var sty = h.scrollTop;
var posx = event.targetTouches[0].pageX;
var stx = h.scrollLeft;
h.scrollTop = sty - (posy - startY);
h.scrollLeft = stx - (posx - startX);
startY = posy;
startX = posx;
});
Source of the second solution is here
The only way I got this to work was by nesting the <iframe> in 2 panels, but this will probably only work if you know the dimensions of the document in the <iframe>, I also placed a transparent <div> over the <iframe> so the touch events still trigger the "scroll events"
root = new Ext.Panel({
fullscreen: true,
layout: 'card',
version: '1.1.1',
scroll: false,
dockedItems: [{ xtype: 'toolbar', title: 'hello'}],
items: [{
xtype: 'panel',
scroll: 'both',
items: [{
id: 'iframe',
layout: 'vbox',
width: '1200px',
height: '1000px',
html: ['<div style="width:1200px;height:1000px;position:fixed;top:0;left:0;background-color:Transparent;float:left;z-index:99;"></div>',
'<iframe style="position:fixed;top:0;left:0;float:left;z-index:1;" width="1200px" height="1000px" src="http://google.com/"></iframe>']
}]
}]
});
So using your code:
this.items = [{
id: 'iframe',
layout: 'vbox',
width: '1200px',
height: '1000px',
html: ['<div style="width:1200px;height:1000px;position:fixed;top:0;left:0;background-color:Transparent;float:left;z-index:99;"></div>',
'<iframe style="position:fixed;top:0;left:0;float:left;z-index:1;" width="1200px" height="1000px" src="' this.theLink + '"></iframe>']
}]
So I have like a list of users on a page. each user name is clickable and it displays the user information in the dialog. Right now I'm using a static length for the list.
I would like jquery to see how big the list of users is and apply the code to the list.
Check out the code here:
$(function() {
var options = {
autoOpen: false,
width: 'auto',
modal: true
};
$([1, 2, 3, 4]).each(function() {
var num = this;
var dlg = $('#dialog-player-' + num).dialog(options);
$('#player-link-' + num).click(function() {
dlg.dialog("open");
return false;
});
});
});
I looked at this page of the documentation: each
What I tried is to select all divs in container "div#parent". Like so:
$(function() {
var options = {
autoOpen: false,
width: 'auto',
modal: true
};
$("div#parent div").each(function() {
var num = this;
var dlg = $('#dialog-player-' + num).dialog(options);
$('#player-link-' + num).click(function() {
dlg.dialog("open");
return false;
});
});
});
But that didn't work. Anybody know of any other way to do this ?
I've noticed a bug in your code and fixed it for you:
$(function() {
var options = {
autoOpen: false,
width: 'auto',
modal: true
};
var num = 1;
$("div#parent div").each(function() {
var dlg = $('#dialog-player-' + num).dialog(options);
$('#player-link-' + num).click(function() {
dlg.dialog("open");
return false;
});
num = num + 1;
});
});
$(function() {
var options = {
autoOpen: false,
width: 'auto',
modal: true
};
$(['George', 'Ralph', 'Carmine', 'Suzy']).each(function(index, val) {
var num = index;
var dlg = $('#dialog-player-' + num).dialog(options);
$('#player-link-' + num).click(function() {
dlg.dialog("open");
return false;
});
});
});
You had the right idea the first time. Just use the index supplied by the each function. No need for a separate counter.
look up 'children' in the docs - I think you might need to cycle through the children of the element using each rather than what you have done. e.g.
$("div#parent").children('div').each(function(){etc...})