I am trying to show relations between items by connecting them via svg lines. When my panel container and the svg element both are at position (0,0) everything works as expected. But if both of them move to another position on the document the coordinate systems seem to get out of sync.
I solved the problem by defining
svg{
min-width:100%;
min-height:100%;
z-index: 2;
top:0;
left:0;
position:fixed;
}
but that feels super hacky.
How can I get my getBoundingClientRect coordinates (absolute coordinates) in sync with the SVG-coordinates (relative to parent position)?
my JS Code:
var panel1 = angular.element(document.querySelector("#panel1"))[0].getBoundingClientRect();
var panel2 = angular.element(document.querySelector("#panel6"))[0].getBoundingClientRect();
element.find("svg").append(SVGTemplatesService.line(
panel1.right,
panel1.top + (panel1.height / 2),
panel2.left,
panel2.top + (panel2.height / 2)));
and the actual drawing happens here:
function SVGTemplatesService(){
var line = function(x1,y1,x2,y2,color){
color = color || '#333';
var newLine = document.createElementNS('http://www.w3.org/2000/svg','line');
newLine.setAttribute('x1',x1);
newLine.setAttribute('y1',y1);
newLine.setAttribute('x2',x2);
newLine.setAttribute('y2',y2);
newLine.setAttribute('style','stroke-width: 5px; stroke: ' + color);
return newLine;
};
return {
line: line
}
}
Related
I have a parent div that contains an image with a bunch of "holes" in it. I have a child div for each hole that defines the exact size of those holes. In the holes, I have text that I've set to position:absolute so that they fit exactly in the holes. However, when I resize the parent div, the contents don't scale at all and they stay the same size. They're also not inside the holes anymore.
User '%' as a unit instead of pixel
<div id="ParentDiv">
<img src="../610614-spring-forest.jpg">
<div class="hole1">Tree1</div>
</div>
#ParentDiv{
max-width:900px;
position: relative;
}
img{
width: 100%;
height: auto;
}
.hole1{
position: absolute;
left:2.75%;
top:23.87%;
}
You can use Viewport Hieght or Viewport Width unit for that case. Below is the example how you could use.
#ParentDiv{
max-width:100vw;
max-height:100vh;
position: relative;
}
img{
width: 100%;
height: auto;
}
.hole1{
position: absolute;
/*left:2.75%;
top:23.87%;*/
left:2.75vw;
top:23.87vh;
}
Try to use similar, hope you will get the desired result.
You could use a CSS property called transform instead of directly changing the size.
When you need to set the size of the image:
// this is the container the image and its text is in
imageContainer = document.getElementById('myDiv');
// original size of image
origX = 400;
origY = 300;
// size you need it to change to
newX = 800;
newY = 600;
/*
Tells CSS to stretch from the top-left edge of the object, so it won't stretch off the left side of the screen
Best way to change value would be percentages
Value of '0 0' sets origin to top-left
Value of '100% 100%' sets origin to bottom-right
*/
imageContainer.style.transformOrigin = '0 0';
//the following is just one string, broken into multiple lines for easy explanation
// tells CSS that the transform type is scale
imageContainer.style.transform = 'scale(' +
// tells CSS how many times normal size to widen image. in this case evaluates to 2, since 800/400 is 2, making image 2x wider
(newX / origX) + ',' +
// tells CSS how many times normal size to heighten image. in this case evaluates to 2, since 600/300 is 2, making image 2x taller
(newY / origY) + ')';
Assuming you have no major performance-sapping functions, this will work great. It's just a little slow if you have loads of stuff going on.
Uncommented:
imageContainer = document.getElementById('myDiv');
origX = 400;
origY = 300;
newX = 800;
newY = 600;
imageContainer.style.transformOrigin = '0 0';
imageContainer.style.transform = 'scale(' + (newX / origX) + ',' (newY / origY) + ')';
I'm trying to rotate a container with javascript and css property transform and transform-origin, the idea is to rotate it around certain coordinates (For example a pinch gesture center between the two fingers), I'm using this simple code (snippet attached) right now to rotate the container and using the onclick event to capture the anchor point. It is working properly as long as you keep clicking without moving the cursor to a different position on the container. There's an issue when you change the click position once the container has been rotated, the expected behavior is to keep track of the transformation and start rotating for that new point, however right now the container is doing an odd jump. I think that some x,y translation need to be added to the container, but i can figure out what's the correct factor to add to the container.
I'm not sure if I've illustrated well the expected behavior, to make sure here's and example: Imagine you pin a note to a surface at certain position, then, you start rotating the note, having the pin as anchor point. Now, after rotating the note a little, you put out the pin (Keeping the note at the same place), then you place the pin on a different position on the note and rotate again with that new anchor point. That's the expected behavior, hope i have explained myself well.
Here's a snippet to show it better, also available on codepen, cheers.
http://codepen.io/vasilycrespo/pen/GZeYpB
var angle = 15,
scale = 1,
origin = { x: 0, y: 0};
var transform = function (e) {
var map = document.getElementById("map");
angle += 15;
map.style.transformOrigin = e.pageX + "px " + e.pageY + "px";
map.style.transform = "rotate("+angle+"deg) scale("+ scale +")";
};
.content{
position: fixed;
top: 0px;
left: 0px;
margin-top:0;
margin-left:0;
background-color: #ccc;
width: 100%;
height: 100%;
}
.square{
position: absolute;
width: 400px;
height: 400px;
background-image: url(http://www.pnas.org/site/misc/images/15-02545.500.jpg);
background-size: cover;
}
<div class="content" onclick="transform(event)">
<div class="square" id="map"></div>
</div>
The problem is that every time you click, the div changes position based on where you click. After the first click, you should save e.pageX and e.pageY, and in the next clicks you should use the saved values. You can change your transform function to this:
var transform = (function () {
var pageX, pageY;
return function(e) {
if (typeof pageX === "undefined") {
pageX = e.pageX
pageY = e.pageY
}
var map = document.getElementById("map"), xr;
angle += 15;
map.style.transformOrigin = pageX + "px " + pageY + "px";
map.style.transform = "rotate("+angle+"deg) scale("+ scale +")";
}
}())
See updated Code Pen.
So I have been trying endlessly to try and do something similar too what this site is doing (http://whois.domaintools.com/). I'm trying to get a webpage, so wherever the mouse moves over the webpage, that kind of effect follows it (I'm sorry I don't know what I would call the effect).
I've read how to ask questions on here, but I don't know what too look for so it's difficult for me to attempt this. So far this link (http://p5js.org/learn/demos/Hello_P5_Drawing.php) I've used the code from this and played around with it but i'm just puzzled as too how I would go about doing this.
Thanks for any help, I've been banging my head against a brick wall for a good couple of days now.
This seems to be some kind of particle system. I would start the following way: First create a class for a particle, it should have a random x and y coordinate, and it should change it's postion periodically to a random new postion. Then create a lot of instances of the particle and distribute them over the page.
http://jsfiddle.net/aggoh0s1/3/
/* each particle will move in a 100px100px square */
var gutterWidth = 100;
/* class definition */
var Particle = function(x, y) {
var t = this;
t.x = x;
t.y = y;
t.elem = $('<div class="particle" />');
t.elem.css({ left: x+"px", top: y+"px"});
$('body').append(t.elem);
/* create a new position every 500-1000 milliseconds */
var milliSecs = 500 + Math.random() * 500;
t.ptinterval = setInterval(function() {
var dx = Math.round(Math.random() * gutterWidth);
var dy = Math.round(Math.random() * gutterWidth);
t.elem.animate({left: (t.x + dx)+"px", top: (t.y + dy) + "px"}, 600);
}, milliSecs);
};
/* create a 1000px1000px area where particles are placed each 100px */
var particles = [];
var newParticle;
for(var x = 0; x < 1000; x = x + gutterWidth) {
for(var y = 0; y < 1000; y = y + gutterWidth) {
newParticle = new Particle(x,y);
particles.push(newParticle);
}
}
CSS:
.particle {
width: 2px;
height: 2px;
background-color: black;
position: absolute;
}
Using this logic, you could also use a canvas to display the particles instead of a html div like it is done on whois.domaintools.com. The next step should be to connect the particles with lines to each other, and after that some code should hide all particles that are some distance away from the mouse position.
I've developed the following solution for the effect which you are referring. This is done using jQuery using the event mousemove(). Bind this event to your body where the content is.
Method :
Create an element with the following css on your body. You can create the element onthefly using jQuery as well.
<div class='hover'></div>
CSS
.hover{
position:absolute;
width:100px;
height:100px;
background-color:#fff;
}
The add the following code to your page.
$('body').mousemove(function(event){
$('.hover').css({
'top' : event.pageY,
'left': event.pageX
})
});
The above code will bind an event to your mouse move and I change the element position according to the mouse coordinates.
This fiddle shows a running example
I've given you the basic idea of the solution! You will have to medle with the css and jquery to add the looks and feels of the effect which you refer to.
See the simple example
<img id="imgMove" src="Images/img1.jpg" height="100" width="100" style="position: absolute;" />
JQuery
$(document).ready(function () {
$(document).mousemove(function (e) {
$("#imgMove").css({ "top": e.pageY - 50, "left": e.pageX - 50 }); // e.pageX - Half of Image height, width
})
})
I've set up a jsfiddle illustrating my situation: http://jsfiddle.net/j5o0w5qc/1/
Basically, I've got three nested HTML elements: a viewport div on the outside, a stage div in the middle, and a canvas on the inside. The stage div provides a perspective setting for 3d transformations applied to the canvas. The viewport has overflow: hidden; so we don't see anything outside of the viewport. It also has a listener attached, listening for mousedown.
In my actual app that I'm building, the canvas might be transformed to any arbitrary 3d transformation, involving translation and rotation in 3d space. What I would like to happen is for the viewport div to intercept a click, and draw a spot on the canvas in the place you clicked. I'm intercepting the event with the viewport div, and I'm using offsetX and offsetY in Chrome. This works great for Chrome, but I know I can't rely on offsetX and offsetY in other browsers, so I'd like to use pageX and pageY, normalized via jQuery, but I'm not sure quite how to do that.
What I've currently got in the jsfiddle works great in Chrome, except when you click in the viewport NOT on the canvas. When you click on the canvas, it draws a dot there, regardless of the canvas's transformation. Chrome is doing all the hard work and giving me exactly what I want with offsetX and offsetY. However, when you click in the viewport NOT on the canvas, I guess it's giving me offsetX and offsetY values relative to the viewport, rather than the canvas, and then interpreting that and drawing a dot on the canvas. For example, if I transform the canvas and then click in the upper right corner of the viewport, a dot appears in the upper right corner of the canvas, regardless of where that corner actually appears on the page.
In Firefox, however, it works great as long as there is no transformation applied to the canvas, but as soon as the canvas is transformed, all of a sudden, the dot being drawn is displaced, and I can't figure out how to take my pageX and pageY values and figure out exactly where in the canvas I am clicking.
Does anyone have any brilliant solutions? I've been bashing my head against this problem for far too long. I'm pretty sure I need to manually calculate some 3d transformation matrices or something, and I've spent hours writing methods to return the inverse of a matrix, and to multiply a matrix by a vector, and all sorts of stuff, but none of it has actually solved the problem for me, and I'm not sure what I'm missing.
Stackoverflow says code is required with jsfiddle links, so here's all my code:
HTML:
<div id="viewport">
<div id="stage">
<canvas id="myCanvas" width="300" height="300"></canvas>
</div>
</div>
<div id="stuff">
<button onclick="transformMe()">Transform</button>
<input id="blah" type="text" size="45"></input>
</div>
CSS:
#viewport, #stage, #myCanvas {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 0;
}
#viewport {
border: 1px solid #000;
overflow: hidden;
}
#stage {
perspective: 1000px;
transform-style: preserve-3d;
}
#myCanvas {
background-color: green;
transform-style: preserve-3d;
}
#stuff {
position: absolute;
top: 350px;
}
Javascript:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.pageX - $('#myCanvas').offset().left;
ypos = e.pageY - $('#myCanvas').offset().top;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
For Firefox, you can use event.layerX and event.layerY. Think of them as Firefox's versions of offsetX & offsetY.
DEMO: http://jsfiddle.net/dirtyd77/j5o0w5qc/3/
JAVASCRIPT:
var counter = 0;
$('#viewport').mousedown(function _drawOnCanvas (e)
{
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var xpos, ypos;
if (typeof e.offsetX=='undefined')
{
xpos = e.originalEvent.layerX;
ypos = e.originalEvent.layerY;
}
else
{
xpos = e.offsetX;
ypos = e.offsetY;
}
ctx.fillRect(xpos-5, ypos-5, 10, 10);
});
function transformMe()
{
counter++;
var angle = (counter * 30) % 360;
$('#myCanvas').css('transform','perspective(1000px) rotate3d(5,6,7,' + angle + 'deg)');
$('input').val('counter: ' + counter + ', angle: ' + angle);
};
If you change viewport to myCanvas in line 3 of the either Kyle S or Dom's jsfiddles:
$('#myCanvas').mousedown(function _drawOnCanvas (e)
it no longer places a dot when you "click in the viewport NOT on the canvas."
It seems there's a new issue with Firefox - if there's a transformation it only lets you paint on half ( the bottom left of diagonal - but depends on transformation ).
It was hard to explain with words, so I tried to explain with graphics.
There is a div here with its style.
Now if I change its width with 400px here...
because of it is a transformed (rotated) object, something happens and "TOP-LEFT" corner of it, moves down.
Now I want to keep its "TOP-LEFT" position fixed. But I couldnt find a correct correlation to fix it. I guess I need a trigonometric formula using rotation angle.
Also I know it is related with 'scale' and 'transform-origin' and can be easily done with them but I dont want to use any other transformation parameters. Especialy 'transform-origin' because of lack of browser support.
Does anybody here who can help me with the correlation which will be used in JavaScript to fix its corner. Maybe getBoundingClientRect() can be used for this.
Here is the FIDDLE
Thank you.
CSS transforms are really matrices, where transforming the elements are done with
matrix(a, b, c, d, tx, ty).
Then someone clever figured out it would be too complicated for webdesigners to understand such a matrix, so they added shorthand solutions, like transform: rotate() etc.
In other words, if you view the computed styles, there won't be a style whith the rotated degrees, and you can't do element.style.transform and get the rotation angle back again, all you'll get is the matrix.
Since the browsers use a matrix, all browsers that support CSS transform, also support changing the origin of that transform, so if you can rotate the element, you can change the origin.
The exception is Microsoft's filters, but even there you can rotate and change the origin, it's just a little more complicated to figure out.
As it makes no sense to not just change the origin of the transformation, and calculating it yourself would do the exact same thing, only a hundred times more complicated, you should really just add this to the CSS to solve the issue
-moz-transform-origin: 0 0;
-o-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
FIDDLE
Just to confirm this, looking at MDN, at the bottom of the following pages, you'll find browser support, and it's just about the same for transform and transform-origin, as you generally never have one without the other
https://developer.mozilla.org/en-US/docs/Web/CSS/transform
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin
As a final note, if it were me, I wouldn't even worry about IE8 and below, as those users are probably used to things looking weird these days anyway.
If you don't want to use transform-origin you can do this :
FIDDLE
$(function () {
var isScaled = false;
$('#box').on('click', function () {
if (isScaled) {
isScaled = false;
$(this).width('200').css({'top':'50px','left':'50px'})
} else {
isScaled = true;
$(this).width('400').css({'top':'24px','left':'46px'});
}
})
});
As other people has stated, you can use transform-origin. However, if you still want to do it via Javascript, I've done it for you in this jsfiddle.
Basically, what I do is to calculate the rotated position of the top left corners of each figure using the matrix transform for rotations (simplified), assuming the center point of the figures as (0, 0), which is, basically, what the browser does. Once I calculate the new positions for the corners, I calculate the difference, and substract that difference from the original left and top positions. I hope you find it instructive.
$(function () {
var isScaled = false;
var box = $('#box');
box.on('click', function () {
if (isScaled) {
isScaled = false;
$(this).width('200');
placeBox(400, 200);
} else {
isScaled = true;
$(this).width('400')
placeBox(200, 400);
}
});
var left = parseInt(box.css('left'));
var top = parseInt(box.css('top'));
var angle = (345 / 360) * 2 * Math.PI; //in radians;
function placeBox(oldWidth, newWidth) {
var midHeight = box.height() / 2;
var midOldWidth = oldWidth / 2;
var midNewWidth = newWidth / 2;
var cos = Math.cos(angle);
var sin = Math.sin(angle);
//rotation center coordinates
var cx1 = left + midOldWidth;
var cx2 = left + midNewWidth;
var cy = top + midHeight;
var mx1 = -midOldWidth * cos + midHeight * sin;
var my1 = -midOldWidth * sin - midHeight * cos;
var mx2 = -midNewWidth * cos + midHeight * sin;
var my2 = -midNewWidth * sin - midHeight * cos;
var difX = cx2 + mx2 - cx1 - mx1;
var difY = my2 - my1;
//now, position the element where it should:
box.css({
left: (left - difX) + 'px',
top: (top - difY) + 'px'
});
}
})
This Fiddle is showing the problem: http://jsfiddle.net/y343Z/19/
If you change the "shadow" size, it is moving alongside it's X Y axis.
Here one possibile soution.
http://jsfiddle.net/y343Z/18/
Just place shadow inside of the tranformed element:
<div id="box">
<div id="box-shadow" style="width:400px;"></div>
</div>
With this CSS:
#box {
width:200px;
height:200px;
position:absolute;
top:50px;
left:50px;
-moz-transform:rotate(345deg);
-webkit-transform:rotate(345deg);
-o-transform:rotate(345deg);
-ms-transform:rotate(345deg);
}
#box-shadow {
width: inherit;
height: inherit;
background-color:silver;
position:absolute;
opacity: 0.3;
}
#box {
background-color:orange;
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
-moz-opacity: 0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
}
Just to clarify: I know that is not desired for a real shadow, since this has to be outside of the transformed box. But i think your shadow object is a "helper" that contains handles like in your screenshot.
Edit:
As other user posted, you may also use transform-origin: http://jsfiddle.net/y343Z/20/
transform-origin: left center 0;