I'm trying to draw a diagonal line with CSS and JS between to elements like this.
I have two divs that I know both of their left and top coordinates.
<div class='wrapper'>
<div class='point' style='left: 40px; top: 40px;'></div>
<div class='point' style='left: 260px; top: 120px;'></div>
<div class='line'></div>
</div>
But how to calculate rotation degree and height value here?
const point1 = document.getElementsByClassName('point')[0]
const point2 = document.getElementsByClassName('point')[1]
const line = document.getElementsByClassName('line')[0]
const lineLeft = parseInt(point1.style.left) + point1.clientWidth
const lineTop = parseInt(point1.style.top) + point1.clientHeight
line.style.left = `${lineLeft}px`
line.style.top = `${lineTop}px`
line.style.height = '?'
line.style.transform = 'rotate(?deg)'
Here's the CodePen version of it.
If you have to do this via CSS and Javascript, it is possible but not ideal. You'll have to do some extra work to calculate the angle between the points and the distance between your points. Take a look at the sample below:
const point1 = document.getElementsByClassName('point')[0]
const point2 = document.getElementsByClassName('point')[1]
const line = document.getElementsByClassName('line')[0]
// Find the points based off the elements left and top
var p1 = {x: point1.offsetLeft, y: point1.offsetTop};
var p2 = {x: point2.offsetLeft, y: point2.offsetTop};
// Get distance between the points for length of line
var a = p1.x - p2.x;
var b = p1.y - p2.y;
var length = Math.sqrt( a*a + b*b );
// Get angle between points
var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
// Get distance from edge of point to center
var pointWidth = point1.clientWidth / 2;
var pointHeight = point1.clientWidth / 2;
// Set line distance and position
// Add width/height from above so the line starts in the middle instead of the top-left corner
line.style.width = length + 'px';
line.style.left = (p1.x + pointWidth)+ 'px';
line.style.top = (p1.y + pointHeight) + 'px';
// Rotate line to match angle between points
line.style.transform = "rotate(" + angleDeg + "deg)";
.wrapper {
width: 320px;
height: 180px;
position: relative;
border: 1px solid #aaa;
background-color: #eee;
}
.point {
width: 20px;
height: 20px;
position: absolute;
background-color: #555;
}
.line {
position: absolute;
height:2px;
background-color: #d63030;
transform-origin: left;
}
<div class='wrapper'>
<div class='point' style='left: 40px; top: 40px;'></div>
<div class='point' style='left: 260px; top: 120px;'></div>
<div class='line'</div>
</div>
Related
I trying to create a map framework for some games and i have a problem with recalc position of marker. Look url to test, with wheel you can resize div with image but the dot red not come back to own position. Sorry but im new on this y trying to learn more about js and css. Thanks
$('.map-live').css('width', "928px");
$('.map-live').css('height', "928px");
$('.map-live').css('background-size', "100%");
$('.map-live').bind('mousewheel DOMMouseScroll', function(event) {
var divSize = $('.map-live').css('width');
console.log(divSize);
divSize = divSize.replace('px', '')
divSize = parseInt(divSize);
console.log("oldSize: " + divSize);
var delta_px = event.originalEvent.wheelDelta > 0 ? (divSize + (divSize * 0.15)) : (divSize - (divSize * 0.15));
console.log("NewSize: " + delta_px);
$(this).css('width', delta_px + "px");
$(this).css('height', delta_px + "px");
$(this).css('background-size', "100%");
UpdatePoints();
});
$(function() {
$("#map-live").draggable();
});
document.getElementById('map-live').addEventListener('click', printPosition)
function getPosition(e) {
var rect = e.target.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
return {
x,
y
}
}
function printPosition(e) {
var position = getPosition(e);
console.log('X: ' + position.x + ' Y: ' + position.y);
var divX = parseInt($('.map-live').css('width').replace('px', ''));
var divY = parseInt($('.map-live').css('height').replace('px', ''));
var vhX = (position.x / divX) * 100;
var vhY = (position.y / divY) * 100;
console.log('vhX: ' + vhX + ' vhY: ' + vhY);
}
function UpdatePoints() {
$('.point').css('top', '2.477565353101834vh');
$('.point').css('left', '2.477565353101834vh');
$('.point').css('position', 'absolute');
}
body {
margin: 0;
overflow: hidden;
}
.map-live {
position: absolute;
left: 10px;
z-index: 9;
background-image: url(https://i.ibb.co/d2y5G1y/map.jpg);
width: 222px;
height: 222px;
transition: all 0.2s linear;
}
.point {
position: absolute;
left: 2.477565353101834vh;
top: 2.477565353101834vh;
width: 10px;
height: 10px;
background-color: red;
border-radius: 50%;
}
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div class="map-live ui-widget-content" id="map-live">
<div class="point"></div>
</div>
jsfiddle.net/f84mto52
Someone can correct me, but I believe your use of position: absolute is what is making the <div class="point"></div> stay in place.
Your UpdatePoints is setting always the same position in the div. With 'vh' you are calculating and absolute position proportional to viewport, no to parent container.
So, you are zooming the background image but the position (x, y) will be always be (x, y), positions are not zoomed. You need to recalculate which is the new position.
So you need to calculate new position.
function UpdatePoints(){
var divW = parseInt($('.map-live').css('width').replace('px',''));
var divH = parseInt($('.map-live').css('height').replace('px',''));
var topPosition = (2.477565353101834 / 928) * divH;
var leftPosition = (2.477565353101834 / 928) * divW;
$('.point').css('top', topPosition+'vh');
$('.point').css('left', leftPosition+'vh');
$('.point').css('position', 'absolute');
}
Also, instead using 'vh' I recommend to calculate the px position instead. I have added the already calculated delta_px parameter to UpdatePoints function:
<style>
.point {
position: absolute;
left: 22.99180647678502px;
top: 22.99180647678502px;
width: 10px;
height: 10px;
background-color: red;
border-radius: 50%;
}
</style>
<script>
function UpdatePoints(delta_px){
var position = (delta_px/100)*2.477565353101834;
$('.point').css('top', position+'px');
$('.point').css('left', position+'px');
$('.point').css('position', 'absolute');
}
</script>
Also, here we are calculating the top-left position of the .point element, not the position for the center. As it is a circle, it work fine, but if you use any other shape the position translation should be calculated from its center.
I recommend to do some research about how to translate elements. You can start here:
Calculating relative position of points when zoomed in and enlarged by a rectangle!
Zoom in on a point (using scale and translate)!
How do I effectively calculate zoom scale?!
I am writing an image viewer JS component and i'm currently stuck on the calculation for the zooming in. I figured out the zooming in or out on one point (instead of just the center) using css transforms. I'm not using transform-origin because there will be multiple points of zooming in or out, and the position must reflect that. However, that's exactly where i'm stuck. The algorithm i'm using doesn't calculate the offset correctly after trying to zoom in (or out) when the mouse has actually moved from the initial position and I can't for the life of me figure out what's missing in the equation.
The goal is whenever the mouse is moved to another position, the whole image should scale with that position as the origin, meaning the image should scale but the point the mouse was over should remain in its place.
For reference, look at this JS component. Load an image, zoom in somewhere, move your mouse to another position and zoom in again.
Here's a pen that demonstrates the issue in my code: https://codepen.io/Emberfire/pen/jOWrVKB
I'd really apreciate it if someone has a clue as to what i'm missing :)
Here's the HTML
<div class="container">
<div class="wrapper">
<div class="image-wrapper">
<img class="viewer-img" src="https://cdn.pixabay.com/photo/2020/06/08/17/54/rain-5275506_960_720.jpg" draggable="false" />
</div>
</div>
</div>
Here's the css that i'm using for the component:
* {
box-sizing: border-box;
}
html, body {
width: 100%;
height: 100%;
margin: 0;
}
.container {
width: 100%;
height: 100%;
}
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
transition: all .1s;
position: relative;
}
.image-wrapper {
position: absolute;
}
.wrapper img {
position: absolute;
transform-origin: top left;
}
And here's the script that calculates the position and scale:
let wrapper = document.querySelector(".wrapper");
let image = wrapper.querySelector(".image-wrapper");
let scale = 1;
image.querySelector("img").addEventListener("wheel", (e) => {
if (e.deltaY < 0) {
scale = Number((scale * 1.2).toFixed(2));
let scaledX = e.offsetX * scale;
let scaledY = e.offsetY * scale;
imageOffsetX = e.offsetX - scaledX;
imageOffsetY = e.offsetY - scaledY;
e.target.style.transform = `scale3d(${scale}, ${scale}, ${scale})`;
image.style.transform = `translate(${imageOffsetX.toFixed(2)}px, ${imageOffsetY.toFixed(2)}px)`;
} else {
scale = Number((scale / 1.2).toFixed(2));
let scaledX = e.offsetX * scale;
let scaledY = e.offsetY * scale;
imageOffsetX = e.offsetX - scaledX;
imageOffsetY = e.offsetY - scaledY;
e.target.style.transform = `scale3d(${scale}, ${scale}, ${scale})`;
image.style.transform = `translate(${imageOffsetX}px, ${imageOffsetY}px)`;
}
});
Thinking a little, I understood where the algorithm failed. Your calculation is focused on the information of offsetx and offsety of the image, the problem is that you were dealing with scale and with scale the data like offsetx and offsety are not updated, they saw constants. So I stopped using scale to enlarge the image using the "width" method. It was also necessary to create two variables 'accx' and 'accy' to receive the accumulated value of the translations
let wrapper = document.querySelector(".wrapper");
let image = wrapper.querySelector(".image-wrapper");
let img = document.querySelector('img')
let scale = 1;
let accx = 0, accy = 0
image.querySelector("img").addEventListener("wheel", (e) => {
if (e.deltaY < 0) {
scale = Number((scale * 1.2));
accx += Number(e.offsetX * scale/1.2 - e.offsetX * scale)
accy += Number(e.offsetY * scale/1.2 - e.offsetY * scale)
} else {
scale = Number((scale / 1.2));
accx += Number(e.offsetX * scale * 1.2 - e.offsetX * scale)
accy += Number(e.offsetY * scale * 1.2 - e.offsetY * scale)
}
e.target.style.transform = `scale3D(${scale}, ${scale}, ${scale})`
image.style.transform = `translate(${accx}px, ${accy}px)`;
});
* {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
}
.container {
width: 100%;
height: 100%;
position: relative;
}
.wrapper {
width: 100%;
height: 100%;
overflow: hidden;
transition: all 0.1s;
position: relative;
}
.image-wrapper {
position: absolute;
}
.wrapper img {
position: absolute;
transform-origin: top left;
}
.point {
width: 4px;
height: 4px;
background: #f00;
position: absolute;
}
<div class="container">
<div class="wrapper">
<div class="image-wrapper">
<img class="viewer-img" src="https://cdn.pixabay.com/photo/2020/06/08/17/54/rain-5275506_960_720.jpg" draggable="false" />
</div>
</div>
</div>
Preview the effect here: JsFiddle
You're missing to calculate the difference in the transform points after an element scales.
I would suggest to use transform-origin set to center, that way you can center initially your canvas using CSS flex.
Create an offset {x:0, y:0} that is relative to the canvas transform-origin's center
const elViewport = document.querySelector(".viewport");
const elCanvas = elViewport.querySelector(".canvas");
let scale = 1;
const scaleFactor = 0.2;
const offset = {
x: 0,
y: 0
}; // Canvas translate offset
elViewport.addEventListener("wheel", (ev) => {
ev.preventDefault();
const delta = Math.sign(-ev.deltaY); // +1 on wheelUp, -1 on wheelDown
const scaleOld = scale; // Remember the old scale
scale *= Math.exp(delta * scaleFactor); // Change scale
// Get pointer origin from canvas center
const vptRect = elViewport.getBoundingClientRect();
const cvsW = elCanvas.offsetWidth * scaleOld;
const cvsH = elCanvas.offsetHeight * scaleOld;
const cvsX = (elViewport.offsetWidth - cvsW) / 2 + offset.x;
const cvsY = (elViewport.offsetHeight - cvsH) / 2 + offset.y;
const originX = ev.x - vptRect.x - cvsX - cvsW / 2;
const originY = ev.y - vptRect.y - cvsY - cvsH / 2;
const xOrg = originX / scaleOld;
const yOrg = originY / scaleOld;
// Calculate the scaled XY
const xNew = xOrg * scale;
const yNew = yOrg * scale;
// Retrieve the XY difference to be used as the change in offset
const xDiff = originX - xNew;
const yDiff = originY - yNew;
// Update offset
offset.x += xDiff;
offset.y += yDiff;
// Apply transforms
elCanvas.style.scale = scale;
elCanvas.style.translate = `${offset.x}px ${offset.y}px`;
});
* {
margin: 0;
box-sizing: border-box;
}
.viewport {
margin: 20px;
position: relative;
overflow: hidden;
height: 200px;
transition: all .1s;
outline: 2px solid red;
display: flex;
align-items: center;
justify-content: center;
}
.canvas {
flex: none;
}
<div class="viewport">
<div class="canvas">
<img src="https://cdn.pixabay.com/photo/2020/06/08/17/54/rain-5275506_960_720.jpg" draggable="false" />
</div>
</div>
For more info head to this answer: zoom pan mouse wheel with scrollbars
I was playing around with JavaScript/canvas and I want my objects color to depend on the distance to its center from current mouse position.This is my current function that gets color every mousemove event:
function getColorFromDistance(node1,node2){
var dist = getDist(node1,node2); //Getting distance;
var cl = (Math.round(255/dist*255)).toString(16); //this needs to be a propper formula
return "#" + cl + cl + cl; //converting to hex
}
Currently I get a blink effect when the distance gets 255.
I need a way to get the colors strength be depended on the distance, so that the further mouse is away from object the more its darken and when mouse is on the objects center its fully white.Well you get the idea.I just need the formula
The formula would be calculate the distance between the two points and get a percentage based on the maximum value (width of canvas/window)
//this would need to be recalulated on resize, but not doing it for demo
var targetElem = document.querySelector("div.x span");
box = targetElem.getBoundingClientRect(),
x = box.left + box.width/2,
y = box.top + box.height/2,
winBox = document.body.getBoundingClientRect(),
maxD = Math.sqrt(Math.pow(winBox.width/2, 2) + Math.pow(winBox.height/2, 2));
document.body.addEventListener("mousemove", function (evt) {
var diffX = Math.abs(evt.pageX-x),
diffY = Math.abs(evt.pageY-y),
distC = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2)),
strength = Math.ceil(255 - (distC/maxD*255)).toString(16),
color = "#" + strength + strength + strength;
targetElem.style.backgroundColor = color;
});
html, body { height: 100%; }
div.x { position: absolute; top: 50%; left:50%; }
span { display: inline-block; width: 20px; height: 20px; border-radius: 50%; border: 1px solid black; overflow: hidden; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>Test</p>
<div class="x"><span> </span></div>
Let's say I have svg, drawn like this:
var myArc = d3.svg.arc()
.innerRadius(width / 5)
.outerRadius(width / 2);
myArc.startAngle(function(d, i) {
return radians * i
});
myArc.endAngle(function(d, i) {
return radians * (i + 1)
...and is later filled with some color, etc. It looks like this:
Now, I want on svg click to get angle degree. Example: I want to get between ~ 310° and 50° when I click on red, 70° or so when I click on yellow etc.
How would I do that? Thanks in advance.
I do some research, didn't find any simpler solution.
I am using the way you mentioned: get center of circle and clicked position, then calculate angle based on those two. I don't think this is too complex to use.
Note: because the circle is not really circle, so there are some numerical errors.
Hope this is useful.
(function($){
var center = {};
var calculateAngle = function(x, y){
var k = Math.abs(y) / Math.abs(x);
var angle = Math.atan(k) * 180 / Math.PI;
if(y * x > 0){
angle = 90 - angle + (y < 0 ? 180 : 0);
} else {
angle = angle + (y < 0 ? 90 : 270);
}
return angle;
}
$(document).ready(function(){
var $container = $('#container');
var $selector = $('#selector');
var $result = $('#result');
center = {
x: $container.width() / 2,
y: $container.height() / 2
}
$selector.css({left:center.x, top:center.y});
$container.on('click', function(e){
var x = e.offsetX;
var y = e.offsetY;
$selector.css({left:x, top:y});
var angle = calculateAngle(x - center.x, center.y - y);
$result.text(angle);
});
});
})(jQuery)
#container{
display: inline-block;
border: 1px solid red;
width: auto;
position: relative;
}
#selector{
position: absolute;
border:1px solid black;
width: 5px;
height: 5px;
border-radius: 50%;
transform: translate(-50%, -50%);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<span id="selector"></span>
<img src="http://i.stack.imgur.com/4X8A7.png">
</div>
<div>
<span>Result: </span>
<span id="result"></span>
</div>
I need to have a code that allows me insert any number os circles inside another circle, keeping the little ones evenly distributed.
The circles were created using border-radius: 50%;
There will be text inside the red circles. Yes, I know. It is complicated.
An expected organisation could be the following:
By some reason I can not insert snippet here, so http://jsfiddle.net/vr60dLth/
HTML
<div class='back'></div>
CSS
.back {
background-color: green;
border-radius: 50%;
position: relative;
}
.front {
position: absolute;
border-radius: 50%;
background-color: red;
}
jQuery
var N = 8, pi = Math.PI, backR = 100, frontR = 15, radius = 70;
$('.back').width(backR * 2).height(backR * 2);
for(var angle = 0; angle < 2 * pi; angle += 2 * pi / N)
{
var s = $('<div class="front">').css({
left: backR - frontR + radius * Math.cos(angle) + 'px',
top: backR - frontR + radius * Math.sin(angle) + 'px',
width: frontR * 2 + 'px',
height: frontR * 2 + 'px'
});
$('.back').append(s);
}