I am moving an <img> element (the octopus) from the large gray <div> above (#large) to the small orange <div> below (#small) using
$(document).on("click", "#large > img", function() {
$(this).appendTo("#small");
});
This works great but I want it to transition smoothly and to 'fly' over so it will slowly interpolate its coordinates and size.
I tried adding a CSS transition
img { transition: all 3s; }
to my <img>, but that won't work as the image is readded to the DOM and not moved.
How can such animation be established?
JS Fiddle
Using the jQuery .append method won't allow you to animate the element between the 2 states.
Here is an example with an animation using CSS transition and the scale() function. This example also uses the transform-origin property to change the position the of the image on the "big" state. Fiddle here.
$(document).on("click", "img", function() {
$(this).toggleClass("big");
});
div {
margin: 20px;
padding: 10px;
}
#large {
width: 600px;
height: 400px;
background-color: gray;
}
#small {
width: 120px;
height: 90px;
background-color: orange;
}
img {
width: 100%;
height: 100%;
transition: transform .3s ease-out;
transform-origin: 0 129px;
}
img.big {
transform: scaleX(5) scaleY(4.4);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="large">
</div>
<div id="small">
<img src="https://ak.picdn.net/assets/cms/5bb580387901a83212e3028ab5a2fa8fb1153d7b-img_offset_2x.jpg" />
</div>
Note that :
you will need to add vendor prefixes to the transition, transform and transform-origin properties depending on the browsers you need to support.
this technique relies on the fact you are using hard values (in pixels). It would be possible to make this responsive (using percent values for widths, margins and paddings) but it will need more calculations.
i made a responsive solution ( so i think ) using JQ . check it out below or in jsFiddle
first i cached all the necessary selectors for cleaner and concise code .
the -20 is because of the div { margin-top:20px}`
there i calculated the TOP offset of both divs in relation to the document, then got the width and height of the small div
in the click function first i got the image's top offset so i could compare that with the #small's offset .
so if the image's distance to top is smaller than the #small's distance to top, it means that the img is in the #large div and so i move it using transform:translate giving it an Y-axis value equal to the TOP offset of the #small Div, so the img offset.top ( iOffset ) will become equal to the #small offset.top ( sOffset )
also adding width and height of the #small div to the image
else ( if iOffset is = or bigger than sOffset ) then it means that the image is not in the large div, so i need to translate it back to the offset of the #large div and add width:100% and height:100%
hope i got it right and explained correctly.
let me know if it helps
var Large = $("#large"),
Small = $("#small"),
lOffset = $(Large).offset().top - 20 + 'px',
sOffset = $(Small).offset().top - 20 + 'px',
sWidth = $(Small).width(),
sHeight = $(Small).height()
$(document).on("click", "img", function() {
var iOffset = $(this).offset().top + 'px'
if (iOffset < sOffset) {
$(this).css('transform', 'translate(0,' + sOffset + ')')
.width(sWidth).height(sHeight)
} else {
$(this).css('transform', 'translate(0,' + lOffset + ')')
.width("100%").height("100%")
}
})
div {
margin: 20px;
padding: 10px;
}
#large {
width: 600px;
height: 400px;
background-color: gray;
}
#small {
width: 120px;
height: 90px;
background-color: orange;
}
img {
width: 100%;
height: 100%;
transition: 5s;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="large">
<img src="https://ak.picdn.net/assets/cms/5bb580387901a83212e3028ab5a2fa8fb1153d7b-img_offset_2x.jpg" />
</div>
<div id="small">
</div>
You need to compute the current dimensions of the image, the target dimensions, and calculate the needed transform.
To make it easier, I will calculate the transform needed to make the new element (the cloned one) look like it is still at the current position.
Later, a standard animation (that just resets scale and position) will do the trick.
I avoided using jQuery so the solution is easier to port
function func (target) {
var image = document.getElementById('image');
var current = image.parentNode;
var rectImage = current.getBoundingClientRect();
var rectTarget = target.getBoundingClientRect();
evalRect (rectImage);
evalRect (rectTarget);
var scaleX = rectImage.width / rectTarget.width;
var scaleY = rectImage.height / rectTarget.height;
var translateX = rectImage.centerX - rectTarget.centerX;
var translateY = rectImage.centerY - rectTarget.centerY;
var dup = image.cloneNode();
var scale = 'scale(' + scaleX + ', ' + scaleY + ') ';
var translate = 'translate(' + translateX + 'px, ' + translateY + 'px) ';
target.appendChild(dup);
dup.style.transform = translate + scale;
current.removeChild(image);
}
function evalRect (rect) {
rect.centerX = rect.left + rect.width * 0.5;
rect.centerY = rect.top + rect.height * 0.5;
}
.container {
border: solid 1px black;
position: relative;
display: inline-block;
}
#container1 {
width: 200px;
height: 100px;
}
#container2 {
width: 400px;
height: 200px;
}
#container3 {
width: 200px;
height: 200px;
}
#image {
background: linear-gradient(45deg, yellow, tomato);
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
animation: adjust 1s forwards;
}
#keyframes adjust {
to {transform: translate(0px, 0px);}
}
<div id="container1" class="container" onclick="func(this)">click me
<div id="image"></div>
</div>
<div id="container2" class="container" onclick="func(this)">click me</div>
<div id="container3" class="container" onclick="func(this)">click me</div>
appendto do not accept animations, but this question maybe helpful for you
appendTo() animation
Just add a transition and change the size and position to match the target. On the transitionend event, append the image to the target element.
// when transition completes
$('img').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(){
// place in container
$('#target').append($('img'));
// set to corner of container
$('img').css({
top: '0',
left: '0'
});
});
// position in corner of target and make size the same
$('img').css({
position: 'absolute',
top: $('#target').offset().top + 'px',
left: $('#target').offset().left + 'px',
height: $('#target').css('height'),
width: $('#target').css('width')
});
#target {
height: 150px;
width: 150px;
border: 1px solid grey;
position: absolute;
top: 350px;
left: 5px;
z-index: 1;
}
img {
position: absolute;
top: 0;
left: 5px;
transition: all 1s;
height: 300px;
width: 300px;
z-index: 5;
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<img src="https://placeholdit.imgix.net/~text?txtsize=33&txt=300%C3%97300&w=300&h=300" />
<div id="target">
</div>
Related
I want to make an animation on my product page. When user clicks "add to cart" the product image will be animated moving and shrinking to the cart icon in the nav bar.
Here is a sample html
$('div.test').on('animationend', (e) => {
$(e.target).remove();
})
//user click
$('div.test').addClass('animateTest');
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : 100px;
left : 1100px;
width : 0;
height : 0
}
}
.animateTest {
animation : toCart 2s;
/* animation-fill-mode: forwards; */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="test">
</div>
The hard part is, since users' viewports vary, I probably need to use javascript to get the cart icon's position(unless I can get it from CSS which I don't think is possible):
whereIsCart = $('#cartIcon').offset()
and I need to do something like
100% {
top : whereIsCart.top;
left : whereIsCart.left;
width : 0;
height : 0
}
But how can I do this?
Or, is there any better practice to achieve the same goal?
It may be easier to use css transitions instead of keyframe animations:
.test {
// ...
transition: transform 1s ease-in-out;
}
// on click
whereIsCart = $('#cartIcon').offset();
$('div.test').css('transform', 'translate(' + whereIsCart.left + 'px, ' + whereIsCart.top + 'px) scale(0)');
When working with CSS in JavaScript you may want to use the CSSOM API; more specifically, its factory functions for unit values, e.g. CSS.px(), CSS.percent().
Note that parts of the CSSOM API are still experimental, e.g. the factory functions. In production, you should make sure that the target browsers support the features you use.
Regardless of using CSS or JS for the animation itself: To get the element's current position in the viewport you can use Element.getBoundingClientRect(), or more generally Element.getClientRects() for all its relevant boxes.
CSS Custom Properties
You can use custom properties for the initial position. You can set them via JavaScript, and even provide a fallback value in CSS.
If you use them for the animating (not as animated) properties, it should just work:
const divTest = document.querySelector("div.test");
// Example for non-empty custom properties
divTest.style.setProperty("--top", CSS.px(20));
divTest.style.setProperty("--left", CSS.px(80));
// Should happen on click:
toCart(divTest);
function toCart(element) {
const rect = element.getBoundingClientRect();
element.style.setProperty("--top", CSS.px(rect.top));
element.style.setProperty("--left", CSS.px(rect.left));
element.classList.add("animateTest");
}
.test {
position: fixed;
top: var(--top, 10%);
left: var(--left, 10%);
width: 200px;
height: 300px;
background: red;
}
#keyframes toCart {
25% {
top: 80%;
left: 50%;
width: 200px;
height: 300px;
}
100% {
top: 10%;
left: 100%;
width: 0;
height: 0;
}
}
.animateTest {
animation: toCart 2s;
}
<div class="test"></div>
Sidenote: If you want to animate custom properties themselves, you have to define the in a #property rule. Otherwise CSS cannot animate it since its type may be anything (animating e.g. from a length to a color is impossible).
Web Animations API
In JavaScript, you can use the Web Animations API, which is essentially CSS animations but in JS.
You can define keyframes, duration, fill-mode and more. Since Animation.finished is a promise, you can simply react to the animation's end via await or Promise.then().
Example:
const divTest = document.querySelector("div.test");
// Should happen on click:
const animation = animateToCart(divTest);
animation.finished.then(() => console.log("Animation finished. This could start a new animation!"));
function animateToCart(element) {
const rect = element.getBoundingClientRect();
const keyframes = [
{
offset: .25,
top: CSS.percent(80),
left: CSS.percent(50),
width: CSS.px(rect.width),
height: CSS.px(rect.height)
}, {
top: CSS.percent(10),
left: CSS.percent(100),
width: 0,
height: 0
}
];
return element.animate(keyframes,
{
duration: 2000,
easing: "ease" // Is default in CSS, but not in Web Animations...
}
);
}
.test {
position: fixed;
top: 10%;
left: 10%;
width: 200px;
height: 300px;
background: red;
}
<div class="test"></div>
Multi-step animations are also easily done with Web Animations, since you can start another animation after the first animation's promise has resolved.
CSS variables sample code...
const
bluElm = document.querySelector('#blue_elm')
, btAnim = document.querySelector('#bt-anim')
, btMovE = document.querySelector('#bt-movE')
, elTest = document.querySelector('.test')
;
btMovE.onclick = () =>
{
bluElm.classList.toggle('move');
}
btAnim.onclick = () =>
{
let rect = bluElm.getBoundingClientRect();
/* change CSS variables values as style Property ------------- */
elTest.style.setProperty('--p_top', `${rect.bottom}px`);
elTest.style.setProperty('--p_left', `${rect.left}px`);
elTest.classList.add('animateTest');
}
elTest.onanimationend = () =>
{
elTest.classList.remove('animateTest');
}
#blue_elm {
position : fixed;
top : 20px;
left : 300px;
width : 20px;
height : 20px;
border-radius : 10px;
background : cornflowerblue;
}
#blue_elm.move {
top : 50px;
left : 150px;
}
.test {
position : fixed;
top : 200px;
left : 600px;
background : red;
width : 200px;
height : 300px;
--p_top : 0; /* CSS variables declaration */
--p_left : 0;
}
.animateTest {
animation : toCart 2s;
}
#keyframes toCart {
25% {
top : 850px;
left : 550px;
width : 200px;
height : 300px;
}
100% {
top : var(--p_top); /* CSS variables usage */
left : var(--p_left);
width : 0;
height : 0
}
}
<button id="bt-anim"> show animation</button>
<button id="bt-movE"> move element +- 150px</button>
<div id="blue_elm"></div>
<div class="test"></div>
How can I position several <img> elements into a circle around another and have those elements all be clickable links as well? I want it to look like the picture below, but I have no idea how to achieve that effect.
Is this even possible?
2020 solution
Here's a more modern solution I use these days.
I start off by generating the HTML starting from an array of images. Whether the HTML is generated using PHP, JS, some HTML preprocessor, whatever... this matters less as the basic idea behind is the same.
Here's the Pug code that would do this:
//- start with an array of images, described by url and alt text
- let imgs = [
- {
- src: 'image_url.jpg',
- alt: 'image alt text'
- } /* and so on, add more images here */
- ];
- let n_imgs = imgs.length;
- let has_mid = 1; /* 0 if there's no item in the middle, 1 otherwise */
- let m = n_imgs - has_mid; /* how many are ON the circle */
- let tan = Math.tan(Math.PI/m); /* tangent of half the base angle */
.container(style=`--m: ${m}; --tan: ${+tan.toFixed(2)}`)
- for(let i = 0; i < n_imgs; i++)
a(href='#' style=i - has_mid >= 0 ? `--i: ${i}` : null)
img(src=imgs[i].src alt=imgs[i].alt)
The generated HTML looks as follows (and yes, you can write the HTML manually too, but it's going to be a pain to make changes afterwards):
<div class="container" style="--m: 8; --tan: 0.41">
<a href='#'>
<img src="image_mid.jpg" alt="alt text"/>
</a>
<a style="--i: 1">
<img src="first_img_on_circle.jpg" alt="alt text"/>
</a>
<!-- the rest of those placed on the circle -->
</div>
In the CSS, we decide on a size for the images, let's say 8em. The --m items are positioned on a circle and it's if they're in the middle of the edges of a polygon of --m edges, all of which are tangent to the circle.
If you have a hard time picturing that, you can play with this interactive demo which constructs the incircle and circumcircle for various polygons whose number of edges you pick by dragging the slider.
This tells us that the size of the container must be twice the radius of the circle plus twice half the size of the images.
We don't yet know the radius, but we can compute it if we know the number of edges (and therefore the tangent of half the base angle, precomputed and set as a custom property --tan) and the polygon edge. We probably want the polygon edge to be a least the size of the images, but how much we leave on the sides is arbitrary. Let's say we have half the image size on each side, so the polygon edge is twice the image size. This gives us the following CSS:
.container {
--d: 6.5em; /* image size */
--rel: 1; /* how much extra space we want between images, 1 = one image size */
--r: calc(.5*(1 + var(--rel))*var(--d)/var(--tan)); /* circle radius */
--s: calc(2*var(--r) + var(--d)); /* container size */
position: relative;
width: var(--s); height: var(--s);
background: silver /* to show images perfectly fit in container */
}
.container a {
position: absolute;
top: 50%; left: 50%;
margin: calc(-.5*var(--d));
width: var(--d); height: var(--d);
--az: calc(var(--i)*1turn/var(--m));
transform:
rotate(var(--az))
translate(var(--r))
rotate(calc(-1*var(--az)))
}
img { max-width: 100% }
See the old solution for an explanation of how the transform chain works.
This way, adding or removing an image from the array of images automatically arranges the new number of images on a circle such that they're equally spaced out and also adjusts the size of the container. You can test this in this demo.
OLD solution (preserved for historical reasons)
Yes, it is very much possible and very simple using just CSS. You just need to have clear in mind the angles at which you want the links with the images (I've added a piece of code at the end just for showing the angles whenever you hover one of them).
You first need a wrapper. I set its diameter to be 24em (width: 24em; height: 24em; does that), you can set it to whatever you want. You give it position: relative;.
You then position your links with the images in the center of that wrapper, both horizontally and vertically. You do that by setting position: absolute; and then top: 50%; left: 50%; and margin: -2em; (where 2em is half the width of the link with the image, which I've set to be 4em - again, you can change it to whatever you wish, but don't forget to change the margin in that case).
You then decide on the angles at which you want to have your links with the images and you add a class deg{desired_angle} (for example deg0 or deg45 or whatever). Then for each such class you apply chained CSS transforms, like this:
.deg{desired_angle} {
transform: rotate({desired_angle}) translate(12em) rotate(-{desired_angle});
}
where you replace {desired_angle} with 0, 45, and so on...
The first rotate transform rotates the object and its axes, the translate transform translates the object along the rotated X axis and the second rotate transform brings back the object into position.
The advantage of this method is that it is flexible. You can add new images at different angles without altering the current structure.
CODE SNIPPET
.circle-container {
position: relative;
width: 24em;
height: 24em;
padding: 2.8em;
/*2.8em = 2em*1.4 (2em = half the width of a link with img, 1.4 = sqrt(2))*/
border: dashed 1px;
border-radius: 50%;
margin: 1.75em auto 0;
}
.circle-container a {
display: block;
position: absolute;
top: 50%; left: 50%;
width: 4em; height: 4em;
margin: -2em;
}
.circle-container img { display: block; width: 100%; }
.deg0 { transform: translate(12em); } /* 12em = half the width of the wrapper */
.deg45 { transform: rotate(45deg) translate(12em) rotate(-45deg); }
.deg135 { transform: rotate(135deg) translate(12em) rotate(-135deg); }
.deg180 { transform: translate(-12em); }
.deg225 { transform: rotate(225deg) translate(12em) rotate(-225deg); }
.deg315 { transform: rotate(315deg) translate(12em) rotate(-315deg); }
<div class='circle-container'>
<a href='#' class='center'><img src='image.jpg'></a>
<a href='#' class='deg0'><img src='image.jpg'></a>
<a href='#' class='deg45'><img src='image.jpg'></a>
<a href='#' class='deg135'><img src='image.jpg'></a>
<a href='#' class='deg180'><img src='image.jpg'></a>
<a href='#' class='deg225'><img src='image.jpg'></a>
<a href='#' class='deg315'><img src='image.jpg'></a>
</div>
Also, you could further simplify the HTML by using background images for the links instead of using img tags.
EDIT: example with fallback for IE8 and older (tested in IE8 and IE7)
Here is the easy solution without absolute positioning:
.container .row {
margin: 20px;
text-align: center;
}
.container .row img {
margin: 0 20px;
}
<div class="container">
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
<div class="row">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
<img src="https://ssl.gstatic.com/s2/oz/images/faviconr2.ico" alt="" width="64" height="64">
</div>
</div>
http://jsfiddle.net/mD6H6/
Using the solution proposed by #Ana:
transform: rotate(${angle}deg) translate(${radius}px) rotate(-${angle}deg)
I created the following jsFiddle that places circles dynamically using plain JavaScript (jQuery version also available).
The way it works is rather simple:
document.querySelectorAll( '.ciclegraph' ).forEach( ( ciclegraph )=>{
let circles = ciclegraph.querySelectorAll( '.circle' )
let angle = 360-90, dangle = 360 / circles.length
for( let i = 0; i < circles.length; ++i ){
let circle = circles[i]
angle += dangle
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth / 2}px) rotate(-${angle}deg)`
}
})
.ciclegraph {
position: relative;
width: 500px;
height: 500px;
margin: calc(100px / 2 + 0px);
}
.ciclegraph:before {
content: "";
position: absolute;
top: 0; left: 0;
border: 2px solid teal;
width: calc( 100% - 2px * 2);
height: calc( 100% - 2px * 2 );
border-radius: 50%;
}
.ciclegraph .circle {
position: absolute;
top: 50%; left: 50%;
width: 100px;
height: 100px;
margin: calc( -100px / 2 );
background: teal;
border-radius: 50%;
}
<div class="ciclegraph">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
Building off #Ana's excellent answer, I created this dynamic version that allows you to add and remove elements from the DOM and maintain proportionate spacing between the elements - check out my fiddle: https://jsfiddle.net/skwidbreth/q59s90oy/
var list = $("#list");
var updateLayout = function(listItems) {
for (var i = 0; i < listItems.length; i++) {
var offsetAngle = 360 / listItems.length;
var rotateAngle = offsetAngle * i;
$(listItems[i]).css("transform", "rotate(" + rotateAngle + "deg) translate(0, -200px) rotate(-" + rotateAngle + "deg)")
};
};
$(document).on("click", "#add-item", function() {
var listItem = $("<li class='list-item'>Things go here<button class='remove-item'>Remove</button></li>");
list.append(listItem);
var listItems = $(".list-item");
updateLayout(listItems);
});
$(document).on("click", ".remove-item", function() {
$(this).parent().remove();
var listItems = $(".list-item");
updateLayout(listItems);
});
#list {
background-color: blue;
height: 400px;
width: 400px;
border-radius: 50%;
position: relative;
}
.list-item {
list-style: none;
background-color: red;
height: 50px;
width: 50px;
position: absolute;
top: 50%;
left: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<ul id="list"></ul>
<button id="add-item">Add item</button>
Here is a version I made in React from the examples here.
CodeSandbox Example
import React, { useRef, useEffect } from "react";
import "./styles.css";
export default function App() {
const graph = useRef(null);
useEffect(() => {
const ciclegraph = graph.current;
const circleElements = ciclegraph.childNodes;
let angle = 360 - 90;
let dangle = 360 / circleElements.length;
for (let i = 0; i < circleElements.length; i++) {
let circle = circleElements[i];
angle += dangle;
circle.style.transform = `rotate(${angle}deg) translate(${ciclegraph.clientWidth /
2}px) rotate(-${angle}deg)`;
}
}, []);
return (
<div className="App">
<div className="ciclegraph" ref={graph}>
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
<div className="circle" />
</div>
</div>
);
}
You can certainly do it with pure css or use JavaScript. My suggestion:
If you already know that the images number will never change just calculate your styles and go with plain css (pros: better performances, very reliable)
If the number can vary either dynamically in your app or just may vary in the future go with a Js solution (pros: more future-proof)
I had a similar job to do, so I created a script and open sourced it here on Github for anyone who might need it. It just accepts some configuration values and simply outputs the CSS code you need.
If you want to go for the Js solution here's a simple pointer that can be useful to you. Using this html as a starting point being #box the container and .dot the image/div in the middle you want all your other images around:
Starting html:
<div id="box">
<div class="dot"></div>
<img src="my-img.jpg">
<!-- all the other images you need-->
</div>
Starting Css:
#box{
width: 400px;
height: 400px;
position: relative;
border-radius: 100%;
border: 1px solid teal;
}
.dot{
position: absolute;
border-radius: 100%;
width: 40px;
height: 40px;
left: 50%;
top: 50%;
margin-left: -20px;
margin-top: -20px;
background: rebeccapurple;
}
img{
width: 40px;
height: 40px;
position: absolute;
}
You can create a quick function along these lines:
var circle = document.getElementById('box'),
imgs = document.getElementsByTagName('img'),
total = imgs.length,
coords = {},
diam, radius1, radius2, imgW;
// get circle diameter
// getBoundingClientRect outputs the actual px AFTER transform
// using getComputedStyle does the job as we want
diam = parseInt( window.getComputedStyle(circle).getPropertyValue('width') ),
radius = diam/2,
imgW = imgs[0].getBoundingClientRect().width,
// get the dimensions of the inner circle we want the images to align to
radius2 = radius - imgW
var i,
alpha = Math.PI / 2,
len = imgs.length,
corner = 2 * Math.PI / total;
// loop over the images and assign the correct css props
for ( i = 0 ; i < total; i++ ){
imgs[i].style.left = parseInt( ( radius - imgW / 2 ) + ( radius2 * Math.cos( alpha ) ) ) + 'px'
imgs[i].style.top = parseInt( ( radius - imgW / 2 ) - ( radius2 * Math.sin( alpha ) ) ) + 'px'
alpha = alpha - corner;
}
You can see a live example here
There is no way to magically place clickable items in a circle around another element with CSS.
The way how I would do this is by using a container with position:relative;. And then place all the elements with position:absolute; and using top and left to target it's place.
Even though you haven't placed jquery in your tags it might be best to use jQuery / javascript for this.
First step is placing your center image perfectly in the center of the container using position:relative;.
#centerImage {
position:absolute;
top:50%;
left:50%;
width:200px;
height:200px;
margin: -100px 0 0 -100px;
}
After that you can place the other elements around it by using an offset() of the centerImage minus the offset() of the container. Giving you the exact top and left of the image.
var left = $('#centerImage').offset().left - $('#centerImage').parent().offset().left;
var top = $('#centerImage').offset().top - $('#centerImage').parent().offset().top;
$('#surroundingElement1').css({
'left': left - 50,
'top': top - 50
});
$('#surroundingElement2').css({
'left': left - 50,
'top': top
});
$('#surroundingElement3').css({
'left': left - 50,
'top': top + 50
});
What I've done here is placing the elements relative to the centerImage. Hope this helps.
You could do it like this: fiddle
Don't mind the positioning, its a quick example
The first step is to have 6 long columnar boxes:
The second step is to use position: absolute and move them all into the middle of your container:
And now rotate them around the pivot point located at the bottom center. Use :nth-child to vary rotation angles:
div {
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
rotate: (360deg / 6) * $n;
}
}
Now all you have to do is to locate your images at the far end of every column, and compensate the rotation with an anti-rotation :)
Full source:
<div class="flower">
<div class="petal">1</div>
<div class="petal">2</div>
<div class="petal">3</div>
<div class="petal">4</div>
<div class="petal">5</div>
<div class="petal">6</div>
</div>
.flower {
width: 300px;
height: 300px;
// We need a relative position
// so that children can have "position:abolute"
position: relative;
.petal {
// Make sure petals are visible
border: 1px solid #999;
// Position them all in one point
position: absolute; top: 0; left: 50%;
display: inline-block;
width: 30px; height: 150px;
// Rotation
transform-origin: bottom center;
#for $n from 0 through 7 {
&:nth-child(#{$n}) {
// Petal rotation
$angle: (360deg / 6) * $n;
rotate: $angle;
// Icon anti-rotation
.icon { rotate: -$angle; }
}
}
}
}
See CodePen
I have this piece of css but I want to change the width in the keyframe with a variable in javascript. How can I do that?
#keyframes test {
100% {
width: 100%;
}
}
Does it have to be a keyframe animation? Typically you would use the CSS transition property for this kind of animation powered by JavaScript, like this:
var width = 50;
document.getElementById('button').addEventListener('click', () => {
width += 50;
document.getElementById('box').style.width = `${width}px`;
});
#box {
background: red;
height: 50px;
width: 50px;
transition: width .5s;
margin-bottom: 1em;
}
<div id="box"></div>
<button id="button">Change Width</button>
If you have a more general animation (that can't be encompassed by just doing a transition) then you can use JS to set a CSS variable.
Taking the example in the question, replace the 100% with a variable:
#keyframes test {
100% {
width: var(--bg);
}
}
and the Javascript you'd have something like:
thediv.style.setProperty('--bg', '60px');
#JohnUleis already answeared correctly. I was too late. But I add just for fun a solution. Is named: How lfar is Rom? ;-)
Cheers
let root = document.documentElement;
const div = document.querySelector('div');
root.addEventListener("mousemove", e => {
div.style.setProperty('--width', e.clientX + "px");
div.innerHTML = e.clientX + ' km';
});
:root {
--width: 100%;
}
div {
background: hotpink;
width: var(--width);
text-align: center;
color: white;
}
<div>how far is rom?</div>
This is the edit of the question to add the following....
animating an object with jquery simply as follows.....
$("div").animate({left: '250px'});
But it would be ideal if instead of left:'250'px it could be the target
div/class something like this....
$("div").animate({'.title-area-main'});
So this question relates to a bit of html , I have a this class within a div called wrapper ....
<div class="logo"><img src="images/mthc/logo-main.png" height="150px" width="420px"></div>
it is defined in css as ....
#wrapper {
left: 50%;
top: 50%;
width:720px;
height:300px;
position:fixed;
margin-top: -50px;
margin-left: -100px;
transform: translate(-25%, -25%);
z-index : 5001;
}
.logo{
float:left;
width:60%;
z-index : 5005;
}
the destination of the class "logo" is to this part of the html page....
<img src="images/mthc/logo.png">
and the css styling for this comes with the class "title-area-title" as per the a href link ....
.metro .tile-area .tile-area-title {
position: fixed;
top: 0;
left: 260px;
border: 0 ;
height: 120px;
width: 800px;
}
I know I could use some really cool css3 animation tween to move the logo into place of the a tag. that would be great if I am using internet explorer version 50000 ... so I am basically looking for a jquery way of doing this and currently have no experience of jquery animation if even possible. Any advice would be really helpful.
To get an element's position :
function getPosition(element) {
var xPosition = 0;
var yPosition = 0;
while (element) {
xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
element = element.offsetParent;
}
return { x: xPosition, y: yPosition };
}
Then you call it like this :
var y = getPosition(document.getElementById('mydivID')).y;
var x = getPosition(document.getElementById('mydivID')).x;
I have a client who wants to have an X-Ray effect that reveals clickable data points. A slider would be used to move a viewing window over an image, that would reveal an x-ray, or secondary image as the slider is moved. I've adapted Eli Kirk's X-Ray effect (http://elikirk.com/2013/12/02/draggable-x-ray-effect-using-css-javascript/) to get what I have so far: http://jsfiddle.net/xfxLx/3/. The jQuery UI portion of it is easy enough:
var artWidth = 300;
$(document).ready(function() {
$('.xraySlider').slider({
slide: function(e, ui) {
var newLeft = (ui.value / 100) * (artWidth - 100);
$('.xrayWindow').css({'background-position': (newLeft * -1) + 'px 0px', "left": newLeft + "px" });
}
});
});
The problem I'm having is making clickable data points that would be revealed by the window as it is slid across the main image. The data points would be fairly simple shapes (like, say, a black circle), that the user could click on once revealed by the x-ray window, to reveal a popup with more info. I've beat my head against the wall trying to come up with a workable solution (if this wasn't bad enough, it all has to work in IE7, so I've ruled out canvas as well).
If this effect won't work under the confines listed (which I've told them might be the case, since I have yet to come up with a viable solution), that's fine, but I just want to make sure I'm not missing anything.
I have changed yoput HTML, setting an inner element to the xray, that will hold the points
<div class="artifact-hold">
<div class="artifact">
<div class="xrayUpper"></div>
<div class="xrayWindow">
<div class="innerXray">
<div class="point" id="brain"></div>
<div class="point" id="heart"></div>
</div>
</div>
<br />
<div class="xraySlider"></div>
</div>
</div>
Then, the JavaScript changes slightly
var artWidth = 300;
$(document).ready(function() {
$('.xraySlider').slider({
slide: function(e, ui) {
var newLeft = (ui.value / 100) * (artWidth - 100);
$('.xrayWindow').css({"left": newLeft + "px" });
$('.innerXray').css({"left": -newLeft + "px" });
}
});
});
And CSS is changed to make the xray clip the contents (with overflow hidden), and the inner has the background image instead of the xraywindow. also, some styling to the points.
.innerXray {
width: 300px;
height: 490px;
position: absolute;
background: url(http://s21.postimg.org/tpg6me1vb/bones.jpg) no-repeat;
background-position: 0px 0px;
}
.point {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: red;
}
#heart {
left: 150px;
top: 130px;
}
#brain {
left: 150px;
top: 30px;
}
.xrayWindow {
width: 100px;
height: 490px;
border: 2px solid rgba(255,255,255,0.5);
position: absolute;
top: 0px;
left: 0px;
overflow: hidden;
}
result