I am building a d3js chart that plots some dots on a gradient scale of agree to disagree. I need it to be able to zoom and pan and I have all of that working except for a rectangle holding a linearGradient. The gradient zooms just as I need it, but it scales up both horizontally AND vertically, expanding past the original 20px height:
Or shrinking excessively:
I tried to use a clip path which is clearly not working, it seems that the clip path scales with the gradient. How can I clamp the rectangle to the axis and keep it the same size?
Here is my block
Thank you!!
There are two primary challenges from what I can see:
the rectangle filled with the gradient scales on the y axis as you note.
the clip path isn't working as intended.
Let's drop the clip path, that'll make things a bit easier. Secondly, let's not scale the rectangle at all when we zoom (we can keep the translate that is applied though).
Now that I've destroyed what we had, let's build it back up.
Instead of scaling the rectangle with transform(scale()) let's modify its width directly. If d3.event.transform.k is the factor at which we were scaling (both x and y), let's just modify the width of the rectangle. Instead of:
gradientScale
.attr("transform","translate("+d3.event.transform.x+","+(height- spacer*3)+")scale("+d3.event.transform.k+")");
Let's use:
gradientScale
.attr("transform", "translate( " +d3.event.transform.x+ " , " + (height - spacer*3) + ")")
.attr("width", width * d3.event.transform.k);
By removing the scaling, the above won't warp any coordinates, which won't lead to stretching of the y axis. It doesn't modify any y coordiantes, so the bar stays where it is height wise with the same height.
It does modify the width - by the same amount we were scaling it before (the rectangle's width at k = 1 is width). This achieves the stretching of the scale as we zoom in. The x translate factor is unchanged.
Here's a bl.ock of the modified code.
My initial thought before looking closely was to try a completely different approach. So, for comparison, here's a completely different approach modifying the axis itself.
Related
Possible duplicates are:
How to calculate translate x and y value when resize after rotate..?
Resize logic to maintain a fixed corner after rotate in javascript
How to resize with fixed corner after rotate?
How to maintain fixed corner while resize a DIV element after rotate
Working in resize and rotate functionality with javascript.
Reference diagram: Rotated 40 deg
The above diagram shows the corner A need to set fixed while resize(using corner C) after rotated to some degrees.
Checked links like this, after finding corners position, how to maintain corner fixed..?
Code in JS fiddle..!
Note: To maintain fixed corner after rotate Canva and Powtoon made some adjustments in translate(x,y) => by Canva and left,top => by Powtoon.
I would like to scale animate an SVG element to fit (preserving aspect ratio) a given area of the SVG.
I know about animate which performs relative animations
var s = Snap("#myelement");
s.animate({ 'transform' : 't100,100s5,5,165,175' },1000);
In principle it should be possible to achieve what I want by computing the parameters of the translation and the scaling.
The problem there is that I do not find accurate documentation of the parameters.
The arguments of t seem to be the relative x,y position and that of s the scale factors and the coordinates of the scale center.
However, how does the combined translation and scaling work? Does the relative translation position scale with the scaling, etc.?
In other words: How do I compute the relative translation and scaling parameters from the coordinates of the upper left and the lower right corner of the animation target element?
Alternatively: Is there a more suitable animate function in Snap?
You show a transform with several parts. The order of these parts is important. If you translate first and scale later, the resulting translation is scaled too. If you scale first and then translate the resulting translation is not affected by the scaling.
The animation you use in Snap.svg is the one I also use. (However I consider migrating to svg.js, since Snap.svg does not play well with Electron for example. I have to do some testing first, though)
Since Snap uses SVG syntax, to solve the problem one needs to understand SVG transformations (see here for an introduction: https://sarasoueidan.com/blog/svg-transformations/). In order to set up a combined SVG transformation it is important to understand that each transformation changes the coordinate system (rather than just the properties of an element in an absolute coordinate frame).
If you combine two transformations, scaling and translation, this means that the parameters of the second transformation depends on the first one.
To achieve a translation and scaling of an element to a given location and size in the coordinates of the ViewBox of an SVG, one can first perform the scaling to the new size choosing the center coordinates for the scaling as the center of the element. Then considerations for the following translations simplify as follows
function startAnimation() {
var svg = Snap("#baseSVG");
/* get the bounding box of the svg */
var bboxSvg = svg.getBBox();
var s = Snap("#element");
/* get the bounding box of the element */
var bbox = s.getBBox();
/* get the required scale factor (assuming that we want to fit the element inside the svg bounding box) */
var scale = Math.min(bboxSvg.width/bbox.width,bboxSvg.height/bbox.height)*0.8;
/* compute the translation needed to bring center of element to center of svg
the scale factor must be taken into account since the translation is based on the coordinate system obtained after the previous scaling */
var tx = (200-bbox.cx)/scale;
var ty = (200-bbox.cy)/scale;
/* perform the animation (make center of scaling the center of element) */
s.animate({ 'transform' : 's' + scale + ',' + scale + ',' + bbox.cx + ',' + bbox.cy + 't' + tx + ',' + ty },1000,mina.bounce);
s.drag();
}
This assumes that your SVG object has id baseSVG and the element you want to transform has id element. It is transformed such that it fits the SVG (adjust the factor 0.8 if you want it larger or smaller). If you know only the coordinates of the corners of the element you must first compute the center coordinates of the target (replace bbox.cx and bbox.cy) and the scale to apply this code snippet. This works in the obvious way in the coordinate frame of baseSVG.
I'm drawing to the canvas using the x/y coords of the mouse, but the line that I'm drawing always draws off a little bit, try drawing on here: http://zachrip.net/widgets/onlineedit/index.html (top left) for an example of what I mean. There is no offset so I do not account for it, so I don't know what the issue is?
The problem here is that you are setting the Canvas Element Size through your CSS, but you do not set the Drawing Surface Size.
The default size of the Drawing Surface is 300px by 150px. Since you do not set it, but set the Element Size, the browser scales the drawing surface size to fit the element. The x and y co-ordinates you get through the mouse event correspond to the Element Size, and not the actual Drawing Surface Size. Which is why you get the offset.
Now, the fiddle that I posted earlier merely had you set the size of Drawing Surface, instead of the Element. And that works, but if you'd rather have different Element and Drawing Surface sizes, then you can also do
function scaleCoords(x, y) {
x = x * DrawingSurfaceSize.width/ElementSize.width;
y = y * DrawingSurfaceSize.height/ElementSize.height;
return {x: x, y: y};
}
Example for second method.
I have an <img> within a <div> which can be moved around using four directional buttons, for example:
The image is obviously larger than its container, hence the directional buttons to move it in different directions.
There is also a zoom control where you can zoom in and out. I set up the scaling method with ease, by just applying a zoom factor as a percentage to the base width and height:
scale: function(zoom)
{
image.width = baseWidth * zoom;
image.height = baseHeight * zoom;
}
// Zoom in 50%.
Scene.scale(1.5);
This is fine however the image scales from top-left, meaning that the image looks like it's getting sucked out towards the top left during a zoom in and spat back out when zooming out.
I'm trying to have the zoom effect apply from the centre of the container, like this:
But I'm finding it hard to get my head around the mathematics required to move the image after scaling applies to give this effect.
The closest I've gotten is to move the image based on the difference between the current zoom and the new zoom level, but it's still slightly off and gives a 'curved' effect when zooming.
Is there a common formula used to reposition an image so that it scales around a different origin (i.e. not top-left (0,0)).
This is what it looks like currently.
You have to take your original coordinates and calculate the center of your original image, that is x_center = x_orig + width_orig / 2; Then you can calculate the new x coordinate of your scaled image: x_new = x_center - width_new / 2. The same applies for y. Then move your scaled image to these new coordinates. If you do it after each time you scale the image, it will look as though it is scaled around its center.
Manipulating the slider until the end, the circle that represents the star disappears or does a different motion. See: jsfiddle.net/NxNXJ/13 Unlike this: astro.unl.edu/naap/hr/animations/hrExplorer.html
Can you help me?? Thanks
When you supply a big luminosity, You're rendering a circle which is millions of pixels tall. The broswer might not render it because it's so big.
However, you are really only interested in a small slice of that big circle - namely, the bit that fits in your tiny window.
At some point, it doesn't make sense to increase the size of the circle, since you can't observe a change in the curvature of the circle - it just looks like a straight vertical line.
This apparent verticality occurs around when x^2 + y^2 = R^2, where R is the radius of the star, Y is half the height of your window, and x is R-1. Solve for R in terms of Y, and you get
function maximumNecessaryRadius(windowHeight){
y = windowHeight / 2;
maxRadius = (y*y - 1)/2;
return Math.round(maxRadius);
}
When resizing the star, check to make sure that its radius doesn't exceed the maximum necessary radius. Rendering it any larger than that is overkill.
Example Implementation