I'm using a some code from https://github.com/dimxasnewfrozen/Panning-Zooming-Canvas-Demos/blob/master/demo12/main.js (demo at http://dayobject.me/canvas/demo12/) to zoom in on an image using the Canvas element.
However, when zooming the jump between one zoom level and the next is too large, so I need to add a scale parameter.
How would I got about doing this?
In your main.js, you can change your zoomLevel here,
mouseWheel: function (e) {
e.preventDefault() // Please add this, coz the scroll event bubbles up to document.
var zoomLevel = 2;
...
if (delta > 0)
{
// ZOOMING IN
var zoomedX = canvasPos.deltaX - (canvasZoomX - canvasPos.deltaX);
var zoomedY = canvasPos.deltaY - (canvasZoomY - canvasPos.deltaY);
// scale the image up by 2
initialImageWidth = initialImageWidth * zoomLevel;
}
else
{
// ZOOMING OUT
var zoomedX = (canvasPos.deltaX + canvasZoomX);
var zoomedY = (canvasPos.deltaY + canvasZoomY);
// scale the image down by 2
initialImageWidth = initialImageWidth / zoomLevel;
}
}
Disclaimer: this ruins the zoomedX and zoomedY values. You gotta fix them :)
It seams to me as if the algorithm always takes half of the dimension befor the zoom. At the end of you code you see it in main.js the mouseWheel function:
initialImageWidth = initialImageWidth * 2;
the width is divided by 2 so just change the used value.
You said the step used to zoom in and out is too big.
So I suggest that you generate the new value by using the dimensions of the image you want to zoom. For example you take a percentage of the biggest dimension of the current image.
That's how you can implement a zoom function that zooms according to the dimensions of the image
Related
I am trying to resize my markers every time the map is zoomed in or out.
Currently I am using this approach:
Iterate all markers in zoomend method.
get current icon size
Perform some calculation to get the new marker size according the zoom size.
set the new dimension to the icon object.
map.on('zoomend', function() {
zoomEndLevel = map.getZoom();
var difference = zoomEndLevel - zoomStartLevel;
console.log("difference in zoom " + difference);
markerArray.forEach(function(marker) {
var icon = marker.options.icon;
var oldX = icon.options.iconSize[0];
var oldY = icon.options.iconSize[1];
var newX = getNewIconAxis(oldX, difference);
var newY = getNewIconAxis(oldY, difference);
console.log(newX + " " + newY);
icon.options.iconSize = [ newX, newY ];
marker.setIcon(icon);
});
});
map.on('zoomstart', function() {
zoomStartLevel = map.getZoom();
});
function getNewIconAxis(value, zoomChange) {
if (zoomChange > 0) {
for (var i = zoomChange; i > 0; i--) {
value = value * 2;
}
} else {
for (var i = zoomChange; i < 0; i++) {
value = value / 2;
}
}
return value;
}
Problem :
This code works fine if I zoom in and out 1 level at once. If I scroll in and out my mouse too frequently then this code given strange outputs. Sometimes the marker size becomes too large or too small.
Question :
1) Is this the only way to make the markers resizable on different zoom levels?
2) If yes then what am I missing here or what changes should be made to make it work perfectly.?
Note : Tagging google maps also because it's more of a logical issue with map (either google or leaflet or mapbox) rather than api specific issue.
Looks like there are several previous posts that you guide you:
Mapbox,leaflet: Increase marker size on Zoom
is there a way to resize marker icons depending on zoom level in leaflet?
https://gis.stackexchange.com/questions/171609/resize-divicons-svgs-at-zoom-levels-leaflet
As for your bug, instead of reading the current icon size value at "zoomstart" event, you might better remember the previous zoom value and read it only at "zoomend" event. I am not sure the "zoomstart" event is fired only once when you scroll (to zoom in/out) successively, while the "zoomend" event may be fired only at the very end.
There's this awesome codepen called Gravity Points which I liked very much and wanted to use on my next project but it turns out it only works well if it is the only element on the page. Once you start adding content above and below it, it miscalculates the mouse position.
Take a look at my fork here, I've added the content above it. Notice if the canvas is aligned perfectly with the screen, the gravity points are created in the right spot but if you click on the canvas when you're half way scrolled up, the points are created a few pixels down.
I'm not great with javascript and jquery, although I'm able to understand which functions it's calling and which functions are being used to draw the points but I can't understand where the calculations are happening and how it's related to scroll position. This functions seems to be triggered when left clicked but where does the cursor coordinates come from?
function mouseDown(e) {
for (var i = gravities.length - 1; i >= 0; i--) {
if (gravities[i].isMouseOver) {
gravities[i].startDrag(mouse);
return;
}
}
gravities.push(new GravityPoint(e.clientX, e.clientY, G_POINT_RADIUS, {
particles: particles,
gravities: gravities
}));
}
So can someone take a look at it and give some insights?
<canvas> element has its own coordinate system, which differs from the document one (the one sent by mouseEvents).
What you need to do is to check canvas's bounding box and remove its offset to your mouseEvents coordinates :
canvas.onmousemove = function(mouseEvent){
var rect = canvas.getBoundingClientRect();
var x = mouseEvent.clientX - rect.left;
var y = mouseEvent.clientY - rect.top;
// doSomething with x and y
for (var i = gravities.length - 1; i >= 0; i--) {
if (gravities[i].isMouseOver) {
gravities[i].startDrag(mouse);
return;
}
}
gravities.push(new GravityPoint(x, y, G_POINT_RADIUS, {
particles: particles,
gravities: gravities
}));
}
OK, so I have a group of four elements rotating 90 degrees as I want them to, around an origin point in the middle of the four elements.
I would like to scale the top left block before and after spinning as well, outward from said origin point, but I'm having much difficulty doing so.
Here is a fiddle for my sample (read: overly simplified) progress so far:
http://jsfiddle.net/Vac2Q/2843/
The fiddle's javascript:
/* create an svg drawing */
var draw = SVG('drawing')
/* draw rectangle */
var dial = draw.circle(60)
.move(125,125)
.fill('#0099ff')
var rect_yellow = draw.rect(50,50)
.move(100,100)
.fill('gold')
var rect_blue = draw.rect(50,50)
.move(160,100)
.fill('navy')
var rect_black = draw.rect(50,50)
.move(160,160)
.fill('black')
var rect_green = draw.rect(50,50)
.move(100,160)
.fill('green')
var blades = draw.group()
.add(rect_yellow)
.add(rect_blue)
.add(rect_black)
.add(rect_green)
.back()
var angle = 0
var rotation = 90
var spin = document.getElementById('spin')
var spun = 0
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
/* calculate new ending orientation for blades */
angle += rotation
var new_rotate = angle
/* rotate the blade group */
blades.animate(1000, '>')
.rotate(new_rotate, 155, 155)
++spun
})
And here is a slightly more glamorous example of what I'm trying to do re: scaling:
The first issue is being able to determine which blade is in the top left position after a given rotation. The second issue is scaling itself; I've gotten the blade to scale, but then it goes crazy and moves off the screen at the same time. I'm not sure how to get it to scale properly from the specified origin point (the middle of the center circle).
You can use the .after() function to chain animations.
I'm not sure if I am using svg.js correctly, but here's what I did:
var rects = [rect_yellow, rect_green, rect_black, rect_blue];
// define the animations
var enlarge_blade = function() {
rects[spun % 4].animate(250, '<')
.scale(1.25, 1.25)
.translate(-38,-38);
};
function spin_anim() {
rects[spun % 4].animate(250, '>')
.scale(1, 1)
.translate(0,0)
.after(rotate_blades);
};
var rotate_blades = function() {
blades.animate(1000, '>')
.rotate(angle, 155, 155)
.after(function() {
++spun;
enlarge_blade();
title.text('spun ' + spun + ' times');
});
};
// Pre-enlarge the first (yellow) rect
enlarge_blade();
/* make rectangle jump and change color on mouse over */
spin.addEventListener('click', function() {
angle += rotation
spin_anim();
})
Demo here
I see you're using svg.js. I don't know how it treats transforms internally. But in any case. To scale an element, you typically use its center point as a reference. Therefore you should find the center point of each rect and scale it using that point. (I assume svg.js performs the required translation internally).
I've looked everywhere and so far have not found a non-jQuery js to handle this. I would like to avoid using a library for just this one simple task.
I would like to fix three navigation divs ("#header", "#tabs" and "#footer") to viewport left (or alternatively, to the x position of a div "#helper" with "position: fixed; left: 0; top: 0;") -- but not fix y. They can not be vertically fixed.
I've created a working js that forces the divs to reposition based on scrolling, but it's not smooth in the real page (too many dynamic and graphic elements) - I'd like it to either animate smoothly, or mimic fixed-left and not appear to reposition at all.
Anyone who can give pointers or a quick script, or review and modify the script I have made? I've noticed people tend to ask why an obvious solution is not used instead of answering the question... I will be glad to answer, but would prefer help with the actual problem.
Here is a jsFiddle with the problem: http://jsfiddle.net/BMZvt/6/
Thank you for any help!
Smooth animation example:
var box = document.getElementById('box');
var moveTo = function(obj, target) {
// start position
// you should obtain it from obj.style
var cpos = {
x: 0,
y: 0
}
var iv = setInterval(function(){
cpos.x += (target.x - cpos.x) * 0.3; // 0.3 is speed
cpos.y += (target.y - cpos.y) * 0.3; // 0.3 is speed
obj.style.left = Math.floor(cpos.x) + 'px';
obj.style.top = Math.floor(cpos.y) + 'px';
var dist = Math.abs(cpos.y - target.y); // distance (x+y) from destination
dist += Math.abs(cpos.x - target.x); // < 1 = object reached the destination
if(dist < 1) { // here we are checking is box get to the destination
clearInterval(iv);
}
}, 30); // this is also the speed
}
box.onclick = function(){
moveTo(box, {x: 90, y: 75}); // fire this function to move box to specified point
}
Demonstration: http://jsfiddle.net/Qwqf6/5/
Your script is your job, but this is a quick start how to solve animation problem
You can also do some fancy stuff with speed for example use sin(x) to set the speed
Demonstration #2 http://jsfiddle.net/Qwqf6/6/ (very smooth)
Full script here https://gist.github.com/3419179
I don't think there's a straight way to do this...
But here's a way.
First, You need to be able to detect the direction of the scrolling when window.onscroll event happens. You would do this by comparing the current page offsets with the newly acquired page offsets whenever the scroll event happens. (http://stackoverflow.com/questions/1222915/can-one-use-window-onscroll-method-to-include-detection-of-scroll-direction)
Now suppose you know the direction of the scroll, you want to change the styling for the divs depending on the direction of the scroll.
Let FixAtX be the value of the x coordinate that you want to fix your divs at.
Let OriginalY be the y coordinate of the divs.
Also whenever scrolling happens, despite of the direction, you want to remember the pageoffset X and Y. Let's call them OldX and OldY
If scrolling vertically:
Set position value for divs' style to be absolute.
Set top value for divs' style to be OriginalY
Set left value for divs' style to be OldX + FixAtX
If scrolling horizontally:
Set position value for divs' style to be fixed.
set top value for divs' style to be OriginalY - OldY (<- this may be different depending on how the browser computes pageOffset value,)
Set Left value for divs' style to be FixAtX
I think this should work...
Since you are just using browser's rendering for positioning, it should be very smooth!
hope I understood the question correctly.
This is for people who view this post - I wound up going with the solution I initially put together in the jsFiddle that used a simple javascript to mimic fixed x.
The javascript in the first answer was hefty and wound up buggy, and the second answer sounded good but did not work in practice. So, I'm recommending the javascript from the jsFiddle (below) as the best answer to fixed x and fluid y without a javascript library. It's not perfect and has a minimal delay but is the best answer I've found.
function fixLeft() {
function getScrollX() {
var x = 0, y = 0;
if( typeof( window.pageYOffset ) == 'number' ) {
x = window.pageXOffset;
} else if( document.body && ( document.body.scrollLeft) ) {
x = document.body.scrollLeft;
} else if( document.documentElement && ( document.documentElement.scrollLeft) ) {
x = document.documentElement.scrollLeft;
}
return [x];
}
var x = getScrollX();
var x = x[0];
// have to get and add horizontal scroll position px
document.getElementById('header').style.left = x + "px";
document.getElementById('tabs').style.left = x + "px";
document.getElementById('footer').style.left = x + "px";
}
window.onscroll = fixLeft;
I have a #wrapper div and a #grid div nested inside. currently I can scroll around with this function below.
getCursorPos : function(){
// set the empty cursor object
var cursor = {};
//get the offset from the left of the grid container
var grid
//offset loop
$(function getCursorPos(){
grid = $('#grid').offset();
setTimeout(getCursorPos, game.loopSpeed);
});
//continuosly get the position
var that = this;
$(document).mousemove(function(e){
//if game mode is menu exit
if(game.mode === 'menu'){
return;
}
// NOTE: this looks a litle over done but don't remove anything
// its like this because javascript uses floating points
// and so in order to line up to the nearest hunderedth I
// had to make the cursor and div position intergers by
// muliplying by ten. one the two are added I reduced them
// and rounded them.
that.x = Math.round(((e.pageX * 10) - (grid.left * 10)) / 10);
that.y = Math.round(((e.pageY * 10) - (grid.top * 10)) / 10);
});
},
the problem is that the mouse coordinates only update when the mouse moves. is there any way to get the coordinates with out moving the mouse?
You always have the latest up-to-date coordinates of the mouse from the last mouse move, clarify why those are not useful to you.