raphael zoom out with grid - infinite paper size - javascript

I am attempting to create a simple drawing package using Raphael.
For this example, I have defined a paper with a specific size that has a grid drawn to the edges of the paper . In my actual application, the paper is set to the size of the div.
See the fiddle http://jsfiddle.net/davidbe/LhWZ5/
I'm using the raphael.pan-zoom package to support zooming with the scroll wheel and panning with the left mouse button.
page = Raphael(graph_paper, 402, 602);
panZoom = page.panzoom({ initialZoom: 0,
initialPosition: { x: 0, y: 0},
maxZoom: 30,
minZoom: -10});
panZoom.enable();
This works fine when I zoom in, but the problem I am having is that when I zoom out so that the Raphael paper is smaller than the div, the grid lines do not extend to the edge of the div. I'm trying to figure out a way to have the grid lines always displayed to the edges of the div, regardless of my zoom setting. In order to do this, I think that I either need to define a larger paper size when I create the paper so that when I zoom out, I am still seeing part of the original paper. Alternatively, I may need to change the paper size as part of the zooming. Either way, I don't want to see x and y scroll bars for the div.
Another way to look at this is that I am trying to simulate an infinite page size. Any suggestions on how to implement this?

Related

OpenSeadragon zoom issues

I am trying to learn openseadragon zoom functionality, however, not understanding something. I put together a simple example and have few questions:
https://codepen.io/Ivalina/pen/poPbaMv
When page first loads, how can I make sure that when I click zoom out right away, image doesn't get any smaller than it already is. This is confusing, as I would hope it starts at its smallest. (priority)
Another question, I did set the min and max zoom levels, yet when I keep on clicking zoom in, it definitely goes beyond 3 clicks. Same goes for zoom out once zoomed in. Is there a way to limit zoom to specific amount of clicks (preferably 3).
var tileSources = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "//openseadragon.github.io/example-images/duomo/duomo_files/",
Format: "jpg",
TileSize: "256",
Size: {
Width: "13920",
Height: "10200"
}
}
};
var viewer = OpenSeadragon({
id: "seadragonviewer",
prefixUrl: "//openseadragon.github.io/openseadragon/images/",
tileSources: tileSources,
visibilityRatio: 1.0,
constrainDuringPan: true,
minZoomLevel: 0,
maxZoomLevel: 2,
zoomPerClick: 1.5,
gestureSettingsMouse: {
clickToZoom: false,
scrollToZoom: false,
flickEnabled: true
},
gestureSettingsTouch: {
clickToZoom: false
},
visibilityRatio: 1.0,
useCanvas: false
});
This isn't necessarily an answer, per se, but I need more space than a comment, so here goes:
The OSD "zoom" is based on the relationship between the width of the image and the width of the viewer. It is 1 when the image exactly fills the viewer horizontally. If you zoom in enough that the viewer only shows half of the width of the image, the zoom is 2. Zoom in enough that the viewer can only hold a quarter of the width, and the zoom is 4. The maxZoomLevel option is based in those terms.
By default, the furthest you can zoom in is based on the ratio of image pixels to screen pixels specified in the maxZoomPixelRatio option (default 1.1). In other words, if maxZoomPixelRatio is 1, then you can only zoom in enough to see the image pixels 1:1. As you've found, you can override that with maxZoomLevel, but then how close to the actual pixels you can get will depend on the size of your viewer.
At any rate, if what you want is to guarantee three clicks and you don't care about how close you get to the actual pixels, here's a scenario:
If your image naturally fills the viewer on the horizontal dimension, you'll get a starting zoom of 1. If zoomPerClick is set to 2, on the first click your zoom will now be 2. On the second click it'll be 4 (zooms are multiplicative), and on the third zoom it'll be 8. Therefore you should set your maxZoomLevel to 8 in that scenario.
If your image doesn't start with a zoom of 1 (because it's tall enough it doesn't fill out the width of the viewer), you'll have to base the maxZoomLevel on that starting zoom. If you have a different zoomPerClick, you'll have to take that into account.
If you do care about how close to the actual pixels you can get on the third click, you'll need to adjust your zoomPerClick so it's different for every viewer size/image size ratio.
I hope this additional background info helps!
EDIT: here's how you might set the zoomPerClick:
What you need to know is the home zoom (where the user starts with the image zoomed all the way out) and the full zoom (when you're zoomed all the way in to see the pixels of the image 1:1), and then you need to figure out what you have to multiply the home zoom by 3 times (since you want 3 clicks) in order to get to the full zoom. Fortunately you can do this after the image is loaded, so OpenSeadragon can do most of the math for you.
viewer.addHandler('open', function() {
const homeZoom = viewer.viewport.getHomeZoom();
const fullZoom = viewer.viewport.imageToViewportZoom(1);
viewer.zoomPerClick = Math.cbrt(fullZoom / homeZoom);
});
I haven't tested this code, but it should be the basic idea.

Align non-geographical leaflet map maxBounds to the bottom of the container

I am creating a non-geographical map with leaflet, which shows a ski resort.
JSFiddle: https://jsfiddle.net/exophunk/ruzgeqL4/
I am using map.fitBounds(bounds) to fit the map to the container and map.setMaxBounds(bounds) to make sure you can't pan outside the map.
This works perfect when zooming in, but as long as the map is smaller than the viewport height, I would like to "align" the whole thing to the bottom of the container, so the map never moves away from the bottom of the container. As it is a mountain, it is pretty obvious.
I achieved this by adding an offset to the maxBounds, as you can see in this example (blue box = max bounds):
https://jsfiddle.net/exophunk/05cq3rzt/
The problem with this approach is, that you can now pan into the "empty sky" when zooming in, while I would actually like to keep the maxBounds as in the first example, so it also restricts movement upwards.
I think this would mean I would need to resize the max bounds while zooming in maybe? But there, I messed up coordinate systems and containers and wasn't able to do this properly.
How can I make sure the map always sticks to the container bottom while keeping the original max bounds, if possible?
You can overwrite the default wheel zoom and check if the zoom mousepoint is in the bounds, else zoom to the center of the bounds/map:
map.scrollWheelZoom._performZoomOrg = map.scrollWheelZoom._performZoom;
map.scrollWheelZoom._performZoom = function(e){
var mouse = map.scrollWheelZoom._lastMousePos;
var llpixel = map.containerPointToLatLng(mouse)
if(!bounds.contains(llpixel)){
map.scrollWheelZoom._lastMousePos = map.unproject([container.clientWidth/2, mapHeight]);
}
map.scrollWheelZoom._performZoomOrg();
}
https://jsfiddle.net/falkedesign/c04ngftj/

Croppic: how to change behavior of image dragging functionality?

Good day. There is a popular application http://github.com/sconsult/croppic. Project demo: http://www.croppic.net. I am trying to use variant Preload (in demo). If I drag image with rotation angle = 0 or 180, I can reach all areas of image (and see it in crop area).
If I drag image with rotation angle <> 90 (80, 120, etc.) - I have unreachable areas, which I can't drag in crop area with such rotation angle. But I need ability to drag all parts of resized and/or rotated image to the crop area. Can you help me to defeat this restriction or tell, where is it - java initDrag / onImgDrag, cropContainer, imgWrapper? I can't understand, where I can find restrictions/parameters/conditions, which define this behavior.
Illustration:
Or you can repeat my example on Croppic demo page.
You should read the code in the touchmove handler (Line 476).
The image is bound to the container and in the source the image position is reset to fit thouse boundaries every time you drag (starts at Line 489).

Understanding rotation and calculating the top left point in KineticJS

I am working on a page where I can view images. I want to create a rotation tool. I've done that, but, it's not working consistently. When I set up the centre point to rotate by, the image jumps slightly, and it gets worse each time. I was experimenting, and, I have code to add a wedge to the top left corner of my top level group ( so, at 0,0 ). If I rotate the image by 45 degrees and drag it so that half of it is off the left edge of my canvas, then I call getAbsolutePosition on the wedge and on the group, I get these values:
layer.getAbsolutePosition()
Object {x: 104.66479545850302, y: 279.2748571151325}
wedge.getAbsolutePosition()
Object {x: 180.2684127179338, y: -73.48773356791764}
I think this means my y position is actually the bottom of the image, which is off screen.
What I want to do, is calculate the absolute position of the middle of my image, when the mouse moves over it, regardless of it's rotation. I have some code that works out points with rotation, which seems like it works at first, almost, but it just gets more and more broken the more I use the tool. I feel like there's something about how Kinetic is tracking these things and what it's reporting, that I am missing. Any hints would be most appreciated. Tutorials I can read are even better ( yes, I've read everything linked from the KineticJS site and searched the web ).
In a nutshell, the question is, if I have an image inside a group, and it's rotated, how do I work out the centre point of the image, taking the rotation in to account, and how do I set the offset so it will rotate from that point, and stay in the same place ?
Thanks
As you've discovered about KinetiJS:
rotation is easy
dragging is easy
dragging+rotation is difficult
After you drag your image you must reset its rotation point (offsetX/offsetY).
KineticJS makes dragging+rotation more difficult than it has to be.
Resetting the offset points of your image will cause KineticJS to automatically move your image (Noooo!!).
That's what's causing your jumping.
The solution to the "jumping" problem:
When you reset the image's rotation point (offsetX/OffsetY) you must also reset the image's X/Y position.
This code resets both XY and Offsets for an image after dragging:
A Demo: http://jsfiddle.net/m1erickson/m9Nw7/
// calc new position and offset
var pos=rect.getPosition();
var size=rect.getSize();
var offset=rect.getOffset();
var newX=pos.x-offset.x+size.width/2;
var newY=pos.y-offset.y+size.height/2;
// reset both position and offset
rect.setPosition([newX,newY]);
rect.setOffset(size.width/2,size.height/2);

Parallax effect with zoom and rotating

I am currently experimenting with parallax effect that i am planning to implement to my HTML5-canvas game engine.
The effect itself is fairly easy to achieve, but when you add zooming and rotating, things get a little more complicated, at least for me. My goal is to achieve something like this:Youtube video.
As you can see, you can zoom in and out "to the center", and also rotate around it and get the parallax effect.
In my engine i want to have multiple canvases that are going to be my parallax layers, and i am going to translate them.
I came up with something like this:
var parallax = {
target: {
x: Mouse.x,
y: Mouse.y
},
offset: {
x: -ctx.width / 2,
y: -ctx.height / 2
},
factor: {
x: 1,
y: 1
}
}
var angle = 0;
var zoomX = 1;
var zoomY = 1;
var loop = function(){
ctx.canvas.width = ctx.canvas.width; //Clear the canvas.
ctx.translate(parallax.target.x * parallax.factor.x, parallax.target.y * parallax.factor.y);
ctx.rotate(angle);
ctx.scale(zoomX, zoomY);
ctx.translate((-parallax.target.x - parallax.offset.x) * parallax.factor.x, (-parallax.target.y - parallax.offset.y) * parallax.factor.y);
Draw(); //Function that draws all the objects on the screen.
}
This is a very small and simplified part of my script, but i hope that's enough to get what i am doing. The object "parallax" contains the target position, the offset(the distance from the target), and the factor that is determining how fast the canvas is moving away relatively to the target. ctx is the canvas that is moving in the opposite direction of the target.(In this example i am using only one layer.) I am using the mouse as the "target", but i could also use the player, or some other object with x and y property. The target is also the point around which i rotate and scale the canvas.
This method works completely fine as long as the factor is equal to 1. If it is something else, the whole thing suddenly stops working correctly, and when i try to zoom, it zooms to the top-left corner, not the target. I also noticed that if i zoom out too much, the canvas is not moving in the opposite way of the target, but in the same direction.
So my question is: What is the correct way of implementing parallax with zooming and rotating?
P.S. It is important to me that i am using canvases as the layers.
To prepare for the next animation frame, you must undo any previous transforms in the reverse order they were executed:
context.translate(x,y);
context.scale(sx,sy);
context.rotate(r);
// draw stuff
context.rotate(-r);
context.scale(-sx,-sy);
context.translate(-x,-y);
Alternatively, you can use context.save / context.restore to undo the previous transforms.
Adjust your parallax values for the current frame,
Save the un-transformed context state using context.save(),
Do your transforms (translate, scale, rotate, etc),
Draw you objects as if they were in non-transformed space with [0,0] at your translate point,
Restore your context to it's untransformed state using context.restore()/
Either way will correctly give you a default-oriented canvas to use for your next animation frame.
The exact parallax effects you apply are up to your own design, but using these methods will make the canvas return to a normal default state for you to design with.

Categories