Rotate marker in Leaflet - javascript

How can I rotate a marker in leaflet? I will have a lot of markers, all with a rotation angle.
I've tried this solution from runanet/coomsie at Leaflet on GitHub, but nothing happens with my marker:
L.Marker.RotatedMarker= L.Marker.extend({
_reset: function() {
var pos = this._map.latLngToLayerPoint(this._latlng).round();
L.DomUtil.setPosition(this._icon, pos);
if (this._shadow) {
L.DomUtil.setPosition(this._shadow, pos);
}
if (this.options.iconAngle) {
this._icon.style.WebkitTransform = this._icon.style.WebkitTransform + ' rotate(' + this.options.iconAngle + 'deg)';
this._icon.style.MozTransform = 'rotate(' + this.options.iconAngle + 'deg)';
this._icon.style.MsTransform = 'rotate(' + this.options.iconAngle + 'deg)';
this._icon.style.OTransform = 'rotate(' + this.options.iconAngle + 'deg)';
}
this._icon.style.zIndex = pos.y;
},
setIconAngle: function (iconAngle) {
if (this._map) {
this._removeIcon();
}
this.options.iconAngle = iconAngle;
if (this._map) {
this._initIcon();
this._reset();
}
}
});
var rotated = new L.Marker.RotatedMarker([63.42, 10.39]);
rotated.setIconAngle(90);
rotated.addTo(map);
Any other ideas or solutions? (Testing with Firefox 16 on Windows.)

Running the code as it is, the icon will disappear when you try to rotate it in Firefox (try rotating on a mouseclick instead of on load and you will see that the icon appears before you try to rotate it), but I'm willing to bet it will work (the first time) in a webkit browser. The reason is the transform lines:
this._icon.style.WebkitTransform = this._icon.style.WebkitTransform + ' rotate(' + this.options.iconAngle + 'deg)';
this._icon.style.MozTransform = 'rotate(' + this.options.iconAngle + 'deg)';
Firefox also uses CSS transforms to position icons, so before rotation it will have Moztransform will have a value of for example "translate(956px, 111px)". The way the code is now, it will replace that with simply "rotate(90deg)" and Firefox won't know where to place the icon.
You want Moztransform to have a value of "translate(956px, 111px) rotate(90deg)", so if you use this code it will work the first time, like in webkit.
this._icon.style.MozTransform = this._icon.style.MozTransform + ' rotate(' + this.options.iconAngle + 'deg)';
However, it will break on the next rotate, so you really need to set both the translation and rotation in one go, like this:
this._icon.style.MozTransform = L.DomUtil.getTranslateString(pos) + ' rotate(' + this.options.iconAngle + 'deg)';
Then you can get rid of the L.DomUtil.setPosition(this._icon, pos); at the start.

This solution is by far the easiest: https://github.com/bbecquet/Leaflet.RotatedMarker
Note: it only modifies the existing marker, allowing two more options (rotationAngle and rotationOrigin).
The solution works very well. As per the GitHub page, a usage example:
L.marker([48.8631169, 2.3708919], {
rotationAngle: 45
}).addTo(map);

What works very well for me is adding a data-rotate="[angle]" attribute to each marker. This allows you to call the following JQuery statement on each refresh when necessary:
$('.your-marker-class').each(function () {
var deg = $(this).data('rotate') || 0;
var rotate = 'rotate(' + $(this).data('rotate') + 'deg) scale(0.5,0.5)';
$(this).css({
'-webkit-transform': rotate,
'-moz-transform': rotate,
'-o-transform': rotate,
'-ms-transform': rotate,
'transform': rotate
});
});
Works very fast and with hundreds/thousands of markers. Found this method in some other post somewhere on the internets but seemed right to share here also.

If you're using react-leaflet, I built upon this idea (https://github.com/bbecquet/Leaflet.RotatedMarker) to create a React component that extends Marker and accepts both rotation and rotationOrigin as a prop.
// Libs
import L from 'leaflet'
// Components
import { ExtendableMarker } from 'react-leaflet-extendable'
// HOCS
import { withLeaflet } from 'react-leaflet'
const proto_setPos = L.Marker.prototype._setPos
const LeafletMarker = L.Marker.extend({
_setPos(pos: [number, number]) {
proto_setPos.call(this, pos)
this._setRotation(this.options.rotation)
},
_setRotation(rotation: number | null | undefined) {
if (typeof rotation === 'number') {
this._icon.style[L.DomUtil.TRANSFORM + 'Origin'] = this.options.rotationOrigin || 'center'
const transform = this._icon.style[L.DomUtil.TRANSFORM] + ` rotate(${rotation}deg)`
this._icon.style[L.DomUtil.TRANSFORM] = transform
}
},
})
const createRotatedMarker = (pos: [number, number], options: any) => {
return new LeafletMarker(pos, options)
}
class RotatedMarker extends ExtendableMarker {
public createLeafletElement() {
return createRotatedMarker(this.props.position, { ...this.props })
}
}
export default withLeaflet(RotatedMarker)

Related

Mobile Transform Origin Property not consistent with Web

I have an application I am developing that allows users to drop "Pins" on an SVG. These pins can be moved around the underlying SVG and the coordinates are saved in a database. The pins also have a "center of mass" of bottom center, I store the coordinates of the tip of the pin, not the 0,0 origin of the pin icon.
I am trying to implement a functionality, that will allow the pins to show larger when zoomed out of the underlying SVG, and scale smaller when zooming in (think google maps, if you look at a zoomed out map of all restaurants and then zoom in, the pins get smaller and more spread out).
I have this feature working on desktop web, see images below
However, on mobile, the same code causes the pins to exist in a different location, and when zooming I can see them scaling down to the top left point, and not the center bottom like the desktop client.
JS Code that is creating the scaling styles and logic:
if (instance) {
instance.dispose();
instance = panzoom($('#partialDiv')[0]);
var pins = Array.from(document.querySelectorAll('.draggable'));
instance.on('transform', function(pz) {
var transform = instance.getTransform();
pins.forEach(pin => {
if (transform.scale > 10 && transform.scale < 18) {
pin.setAttribute("transform", 'matrix(' +
15 / transform.scale + ', 0, 0, ' +
15 / transform.scale + ', ' +
pin.transform.baseVal[0].matrix.e + ', ' + pin.transform.baseVal[0].matrix.f + ')');
var pinStyle = pin.style;
pin.setAttribute("transform-origin", "" + pin.getBBox().width / 2 + " " + pin.getBBox().height + "0px");
pinStyle.transformBox = 'fill-box';
pin.style['-webkit-transform-origin-x'] = '50%';
pin.style['-webkit-transform-origin-y'] = 'bottom';
pinStyle.transformBox = 'fill-box';
} else if (transform.scale > 18) {
pin.setAttribute("transform", 'matrix(0.8, 0, 0, 0.8, ' +
pin.transform.baseVal[0].matrix.e + ', ' + pin.transform.baseVal[0].matrix.f + ')');
var pinStyle = pin.style;
pin.setAttribute("transform-origin", "" + pin.getBBox().width / 2 + " " + pin.getBBox().height + "0px");
pinStyle.transformBox = 'fill-box';
pin.style['-webkit-transform-origin-x'] = '50%';
pin.style['-webkit-transform-origin-y'] = 'bottom';
} else {
pin.setAttribute("transform", 'matrix(2, 0, 0, 2, ' +
pin.transform.baseVal[0].matrix.e + ', ' + pin.transform.baseVal[0].matrix.f + ')');
var pinStyle = pin.style;
pin.setAttribute("transform-origin", "" + pin.getBBox().width / 2 + " " + pin.getBBox().height + "0px");
pinStyle.transformBox = 'fill-box';
pin.style['-webkit-transform-origin-x'] = '50%';
pin.style['-webkit-transform-origin-y'] = 'bottom';
}
});
});
}
Why would the pins be displayed at a different origin on Chrome Desktop vs. Chrome IOS? Same is true for other desktop browsers and Safari on mobile. I have tried variations of webkit styles but it does not seem to change this behavior. Any advice is much appreciated.
After hours of troubleshooting I learned that there is a bug on IOS webkit 16.2+ that does not allow transform-origin to work with a transform attribute. Only the transform being performed in CSS will work with a transform-origin style.
Useful resource for anyone having an issue similar to me:
https://caniuse.com/mdn-svg_attributes_presentation_transform-origin

Cropping not working on rotated images javascript

I am trying to crop an image after rotation , but unfortunately the crop is being done on original image instead of rotated image.
I am using open cv . Here is my code for rotation -
rotateImage(event) {
var sampleimage = document.getElementById("sample");
console.log(sampleimage.height);
var scope = this;
if (event.target.id === "rotateC") {
rotate = rotate + 90;
// sampleimage.style.transform = "rotate(" + rotate + "deg)";
scope.imageRotate();
}
if (event.target.id === "rotateA") {
rotate = rotate - 90;
sampleimage.style.transform = "rotate(" + rotate + "deg)";
}}
Is there a simple solution for the same. Any help will be appreciated.

Set -webkit-transform and transform using JS

I am trying to change "transform" css property of element, using jQuery:
myBlock.css('-webkit-transform', 'rotate(' + angle + 'deg)');
myBlock.css('-moz-transform', 'rotate(' + angle + 'deg)');
myBlock.css('-ms-transform', 'rotate(' + angle + 'deg)');
myBlock.css('-o-transform', 'rotate(' + angle + 'deg)');
myBlock.css('transform', 'rotate(' + angle + 'deg)');
I want to see all of these properties applied to element, but the result is only
transform: rotate(45ged);
I tried JavaScript style, but it does not help too:
myBlock.style.WebkitTransform = "rotate(" + angle + "deg)";
Applied style is the same, as in previous example.
I found information, that modern jQuery versions (from 1.8) remove prefixes automatically. But why JS-style removes vendor properties?
To write all of these in one "attr('style', 'properties')" string is not a solution, because in this case it removes existing styles.
So the question is: how to apply all transform properties, using JS or jQuery?
Thanks for any help.
Edit: the goal is to have prefixed properties in saved html after executing JS command, not just to execute JS command and rotate div on angle's value.
Perhaps you could first retrieve the style attributes, append your own, and then set it again on the element.
var value = element.getAttribute('style');
value += 'your new style attributes';
element.setAttribute('style', value);
So, I have not found any good solution, except to modify "style" string. Here is it:
myBlock.css('transform', 'rotate(' + angle + 'deg)');
// Fix for old android versions:
var blockStyle = myBlock.attr('style');
if (blockStyle.indexOf('-webkit-transform') === -1) {
blockStyle += " -webkit-transform: rotate(" + angle + "deg);";
}
myBlock.attr('style', blockStyle);
The first line of code overwrites all transform properties, including prefixed. After its execution there will be no -webkit-transform part in style string. Then we add it.
In case of there still will be -webkit-transform in style string somehow after the first command (for me it never happen in modern Chrome, Firefox and Edge), I have else statement, a little bit ugly, but it works.
var blockStyle = myBlock.attr('style');
if (blockStyle.indexOf('-webkit-transform') === -1) {
blockStyle += " -webkit-transform: rotate(" + angle + "deg);";
} else {
var blockStyleArrayOriginal = blockStyle.split('-webkit-transform: rotate(');
blockStyle = blockStyleArrayOriginal[0].trim() + " -webkit-transform: rotate(" + angle + blockStyleArrayOriginal[1].substring(blockStyleArrayOriginal[1].indexOf('deg)'));
}
myBlock.attr('style', blockStyle);

Continuous rotate image onclick of button

To display Image I used colorbox..In that I have add rotate-left and rotate-right to rotate the image..
The code is:
var rotate_right = document.getElementById('cboxRight');
$(rotate_right).on('click', function () {
var cboxphoto = document.getElementsByClassName('cboxPhoto')[0].style;
cboxphoto.setAttribute('-ms-transform', 'rotate(90deg)');
});
var rotate_left = document.getElementById('cboxLeft');
$(rotate_left).on('click', function () {
var cboxphoto = document.getElementsByClassName('cboxPhoto')[0].style;
cboxphoto.setAttribute('-ms-transform', 'rotate(270deg)');
});
It rotate 90deg if I click again on rightrotate button then it wont work..I want to rotate it again when click on button
You're only ever rotating to 90 or 270 degrees. When you click again, it doesn't move as it is already rotated to that angle.
Keep track of the current rotation instead and set the attribute to that value plus or minus 90deg - you can probably clean up the code a bit as well, but something like this fiddle: http://jsfiddle.net/w6ho689e/
var degrees = 0;
$("#cboxRight").on('click', function () {
var $cboxphoto = $('.cboxPhoto');
degrees += 90;
$cboxphoto.css('-ms-transform', 'rotate(' + degrees + 'deg)');
$cboxphoto.css('-webkit-transform', 'rotate(' + degrees + 'deg)');
$cboxphoto.css('transform', 'rotate(' + degrees + 'deg)');
});
$("#cboxLeft").on('click', function () {
var $cboxphoto = $('.cboxPhoto');
degrees -= 90;
$cboxphoto.css('-ms-transform', 'rotate(' + degrees + 'deg)');
$cboxphoto.css('-webkit-transform', 'rotate(' + degrees + 'deg)');
$cboxphoto.css('transform', 'rotate(' + degrees + 'deg)');
});

Rotating on mouse move

Something I've wanted to learn for quite a time now, but haven't been able to figure out.
http://jsfiddle.net/Mobilpadde/Xt7ag/
Then you move the mouse, it follows, which is the easy part, but I want to rotate too, like always look in the direction of the mouse, but not so static, more like, if you move your mouse up, it should kinda rotate first, and then you move the mouse further away, it should begin to follow again (If you know what I mean).
Is that something simple to do, or 3k lines? (Or maybe a jQuery plugin?)
Hiya I got it something more closer by using an old post of mine : demo http://jsfiddle.net/Z3pGQ/3/
I am still working, will flick you more smoother version or if you can improve before me:
Old post: Rotating an element based on cursor position in a separate element
Hope it helps, I am trying to make it smoother now, cheers
Sample code
$(document).ready(function() {
$(document).mousemove(function(e) {
$(".firefly").css({
"top": (e.pageY * 2) + "px",
"left": (e.pageX * 2 + 130) + "px"
});
})
})
var img = $(".firefly");
if (img.length > 0) {
var offset = img.offset();
function mouse(evt) {
var center_x = (offset.left) + (img.width() / 2);
var center_y = (offset.top) + (img.height() / 2);
var mouse_x = evt.pageX;
var mouse_y = evt.pageY;
var radians = Math.atan2(mouse_x - center_x, mouse_y - center_y);
var degree = (radians * (180 / Math.PI) * -1) + 90;
img.css('-moz-transform', 'rotate(' + degree + 'deg)');
img.css('-webkit-transform', 'rotate(' + degree + 'deg)');
img.css('-o-transform', 'rotate(' + degree + 'deg)');
img.css('-ms-transform', 'rotate(' + degree + 'deg)');
}
$(document).mousemove(mouse);
}​
Image
This is going to involve a lot more math than I want to do right now, but you can apply rotations with css easily. Here are the properties for mozilla and webkit, you can see the rest of the (IE,Opera...) at this page. Here is your function with a 120deg rotation applied. You will still need to calculate the proper rotation, and adjust the left and top accordingly.
$(document).mousemove(function(e){
$(".firefly").css({
"top":(e.pageY*2)+"px",
"left":(e.pageX*2+130)+"px",
"-moz-transform": "rotate(120deg)",
"-webkit-transform": "rotate(120deg)"});
})
There is a jQuery plugin for that http://pixelscommander.com/en/iphone-development/rotate-html-elements-with-mouse/

Categories

Resources