I have some issues while making an slideshow - html

I'm trying to make an slideshow, but when it moves to the next (or previous) image, there is nothing on the background.
I would like the image you're going to see appearing behind the one you are seeing, something like when you are shuffling cards.
Aditionally, I would like to have different animations when you click on the right or on the left arrow, to match it's movement.
Here is the code I'm using right now:
Code
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.min.css">
<style>
.mySlides {display: none}
img {vertical-align: middle;}
/* Slideshow container */
.slideshow-container {
overflow: hidden;
max-width: 300px;
position: relative;
margin: auto;
}
/* Next & previous buttons */
.prev, .next {
cursor: pointer;
position: absolute;
top: 50%;
width: auto;
padding: 16px;
margin-top: -22px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
user-select: none;
}
/* Position the "next button" to the right */
.next {
right: 0;
border-radius: 3px 0 0 3px;
}
/* On hover, add a black background color with a little bit see-through */
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.8);
}
</style>
</head>
<body>
<div class="slideshow-container">
<div class="mySlides animated slideInLeft">
<img src="https://pruebas20.webcindario.com/img_snow_wide.jpg" style="width:100%">
</div>
<div class="mySlides animated slideInRight">
<img src="https://pruebas20.webcindario.com/img_nature_wide.jpg" style="width:100%">
</div>
<div class="mySlides slideInRight">
<img src="https://pruebas20.webcindario.com/img_mountains_wide.jpg" style="width:100%">
</div>
<a class="prev" onclick="plusSlides(-1)">❮</a>
<a class="next" onclick="plusSlides(1)">❯</a>
</div>
<script>
var slideIndex = 1;
showSlides(slideIndex);
function plusSlides(n) {
showSlides(slideIndex += n);
}
function currentSlide(n) {
showSlides(slideIndex = n);
}
function showSlides(n) {
var i;
var slides = document.getElementsByClassName("mySlides");
var dots = document.getElementsByClassName("dot");
if (n > slides.length) {slideIndex = 1}
if (n < 1) {slideIndex = slides.length}
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
for (i = 0; i < dots.length; i++) {
dots[i].className = dots[i].className.replace(" active", "");
}
slides[slideIndex-1].style.display = "block";
dots[slideIndex-1].className += " active";
}
</script>
</body>
</html>
Thank you!

The problem is that you are hiding all but the current slide, both with javascript and css.
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
}
.mySlides {display: none}
You would need to remove this, but change the z-index of the slides instead so that the current slide is on top, and at the same time add the slideInRight/slideInLeft classes. You would also need to absolutely position the slides within the container, so that they occupy the same position.

Yes, Mark Fisher is correct. You need to play around with the z-index of the slides. To be honest it would probably be easier to use a plugin such as Slick Slider if you want to have cool transitions etc.
I have created a codepen based on your code. It is not perfect, but perhaps you can get an idea of how to do this. Your slides need to be positioned absolutely within the slide wrapper in order to be "revealed" behind each other when you move between slides. I have used a CSS transition on the position to animate the slide.
.mySlides {position: absolute; left: 0; z-index: 0;transition: left 1s linear; width: 300px; height: 105px;}
https://codepen.io/moorehannah/pen/eqdXby

Related

How can I position the element at very precise pixel on image using Top and Left CSS property

I want to display a dot at specific pixel on image click. I'm displaying it by giving top and left values in %. What happening is the dot isn't moving when clicked another pixel present inside the dot.
When click outside then it is moving. I don't understand why this is happening.
May be it is because there is very small change in top and left values for each pixel.
I've updated CSS for displaying dot within the circle
.hObiiS{
border: solid 1px #303030 !important;
background: rgba(0, 0, 0, 0.3);
border-radius: 50%;
box-sizing: border-box;
box-shadow: none !important;
height: 9px !important;
position: absolute;
transform: translate3d(-50%, -50%, 0);
width: 9px !important;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.hObiiS::before{
position: absolute;
content: '';
width: 1px;
height: 1px;
background-color: rgb(224, 1, 1);
border-radius: 50%;
}
<div class="hObiiS" style="top: 25.4601%; left: 58.6382%;"></div>
Can someone please provide solution to move dot per pixel ?
Here is your problem solution.
let container = document.querySelector('img');
let dot = document.getElementById('dot');
document. addEventListener('click', function( e ) {
if (container === event.target && container.contains(e. target)) {
var parentPosition = getPosition(container);
var xPosition = e.clientX - parentPosition.x - (dot.clientWidth / 2);
var yPosition = e.clientY - parentPosition.y - (dot.clientHeight / 2);
dot.style.left = xPosition + "px";
dot.style.top = yPosition + "px";
}
});
// Helper function to get an element's exact position
function getPosition(el) {
var xPos = 0;
var yPos = 0;
while (el) {
if (el.tagName == "BODY") {
// deal with browser quirks with body/window/document and page scroll
var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
var yScroll = el.scrollTop || document.documentElement.scrollTop;
xPos += (el.offsetLeft - xScroll + el.clientLeft);
yPos += (el.offsetTop - yScroll + el.clientTop);
} else {
// for all other non-BODY elements
xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
yPos += (el.offsetTop - el.scrollTop + el.clientTop);
}
el = el.offsetParent;
}
return {
x: xPos,
y: yPos
};
}
.container {
position: relative;
cursor: "crosshair";
}
#dot {
position: absolute;
top: 0;
left: 0;
width: 10px;
height: 10px;
display: inline-block;
background-color: red;
transform: translate(100, 0);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<img width="200px" alt="" src="https://img.rawpixel.com/s3fs-private/rawpixel_images/website_content/upwk62143495-wikimedia-image.jpg?w=800&dpr=1&fit=default&crop=default&q=65&vib=3&con=3&usm=15&bg=F4F4F3&ixlib=js-2.2.1&s=218f80fbd029cd0fa69b8597ef4928c0" />
<span id="dot" />
</div>
</body>
</html>
Codepen
Your mouse click position (e.clientX and e.clientY) is relative to your browser's top-left corner that's why your click position is not accurate. You can study the details explanation in this article.
Move Element to Click Position
You need to stop the dot from stopping the click going through to the image.
You can use pointer-events for that.
Here's a simple example:
.container {
position relative;
display: inline-block;
width: 30vmin;
height: 30vmin;
}
img {
position: relative;
width: 100%;
height: 100%;
object-fit: cover;
}
.dot {
background: red;
width: 30px;
height: 30px;
border-radius: 50%;
position: absolute;
top: 10%;
left: 10%;
pointer-events: none;
}
<div class="container"><img onclick="alert('I saw the click');" src="https://picsum.photos/id/1015/300/300">
<div class="dot"></div>
</div>

How do you get multiple automatic slideshows on one page?

I am currently following the W3 HTML/CSS tutorial on getting multiple slideshows on one page. The tutorial has examples for multiple slideshows (manual) or ONE automatic slideshow, but no examples for multiple (automatic) slideshows. I attempted to combine the tutorial, keeping in mind that the indexes for each individual slideshow needed to be intact, but no matter what I do, it would not automatically transition.
This is the link to the W3 tutorial: https://www.w3schools.com/howto/howto_js_slideshow.asp(https://www.w3schools.com/howto/howto_js_slideshow.asp)
This is the code that I attempted to change/use to automatically transition the slides:
var slideIndex = [1,1];
/* Class the members of each slideshow group with different CSS classes */
var slideId = ["mySlides1", "mySlides2"]
showSlides(1, 0);
showSlides(1, 1);
function plusSlides(n, no) {
showSlides(slideIndex[no] += n, no);
}
function showSlides(n, no) {
var i;
var x = document.getElementsByClassName(slideId[no]);
if (n > x.length) {slideIndex[no] = 1}
if (n < 1) {slideIndex[no] = x.length}
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
n++; /* WHAT I ADDED */
x[slideIndex[no]-1].style.display = "block";
setTimeout(showSlides(n, x), 2000); /* WHAT I ADDED */
}
The capitalized comment next to the code line is what I added to the tutorial code. Otherwise, the rest of the code is exactly the same as the tutorial with no changes. I have tried setInterval and moving the code in different spots, but nothing works.
Just a couple of changes and you are there. I also added an option to change delays, take a look:
/* Find all slideshow containers */
var slideshowContainers = document.getElementsByClassName("slideshow-container");
/* For each container get starting variables */
for(let s = 0; s < slideshowContainers.length; s++) {
/* Read the new data attribute */
var cycle = slideshowContainers[s].dataset.cycle;
/* Find all the child nodes with class mySlides */
var slides = slideshowContainers[s].querySelectorAll('.mySlides');
var slideIndex = 0;
/* Now we can cycle slides, but this recursive function must have parameters */
/* slides and cycle never change, those are unique for each slide container */
/* slideIndex will increase during each iteration */
showSlides(slides, slideIndex, cycle);
};
/* Function is alsmost same, but now it uses 3 new parameters */
function showSlides(slides, slideIndex, cycle) {
for (i = 0; i < slides.length; i++) {
slides[i].style.display = "none";
};
slideIndex++;
if (slideIndex > slides.length) {
slideIndex = 1
};
slides[slideIndex - 1].style.display = "block";
/* Calling same function, but with new parameters and cycle time */
setTimeout(function() {
showSlides(slides, slideIndex, cycle)
}, cycle);
};
* {
box-sizing: border-box;
}
body {
font-family: Verdana, sans-serif;
}
.mySlides {
display: none;
}
img {
vertical-align: middle;
}
/* Slideshow container */
.slideshow-container {
max-width: 320px;
position: relative;
margin: auto;
}
/* Caption text */
.text {
color: #f2f2f2;
font-size: 15px;
padding: 8px 12px;
position: absolute;
bottom: 8px;
width: 100%;
text-align: center;
}
/* Number text (1/3 etc) */
.numbertext {
color: #f2f2f2;
font-size: 12px;
padding: 8px 12px;
position: absolute;
top: 0;
}
/* The dots/bullets/indicators */
.dot {
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbb;
border-radius: 50%;
display: inline-block;
transition: background-color 0.6s ease;
}
.active {
background-color: #717171;
}
/* Fading animation */
.fade {
-webkit-animation-name: fade;
-webkit-animation-duration: 1.5s;
animation-name: fade;
animation-duration: 1.5s;
}
#-webkit-keyframes fade {
from {
opacity: .4
}
to {
opacity: 1
}
}
#keyframes fade {
from {
opacity: .4
}
to {
opacity: 1
}
}
/* On smaller screens, decrease text size */
#media only screen and (max-width: 300px) {
.text {
font-size: 11px
}
}
<h2>Automatic Slideshow</h2>
<p>Change image every 2 seconds:</p>
<div class="slideshow-container" data-cycle="2000">
<div class="mySlides fade">
<div class="numbertext">1 / 3</div>
<img src="https://placeimg.com/320/240/animals">
<div class="text">Caption Text</div>
</div>
<div class="mySlides fade">
<div class="numbertext">2 / 3</div>
<img src="https://placeimg.com/320/240/nature">
<div class="text">Caption Two</div>
</div>
<div class="mySlides fade">
<div class="numbertext">3 / 3</div>
<img src="https://placeimg.com/320/240/people">
<div class="text">Caption Three</div>
</div>
</div>
<p>Change image every 3 seconds:</p>
<div class="slideshow-container" data-cycle="3000">
<div class="mySlides fade">
<div class="numbertext">1 / 2</div>
<img src="https://placeimg.com/320/240/animals">
<div class="text">Caption 1</div>
</div>
<div class="mySlides fade">
<div class="numbertext">2 / 2</div>
<img src="https://placeimg.com/320/240/nature">
<div class="text">Caption 2</div>
</div>
</div>
Also on JSFiddle
What is most important here is the scope of Javascript variables. I suggest reading about JS Variables, than google javascript scope variables and you may find articles like this one, or that one.

how can I make slideshow images scale proportionately at different viewport sizes?

I found code for a slideshow of images that I really like but that didn’t resize at different browser sizes. I tried using the vh property to make that happen but it didn’t work – I couldn’t get the images to scale proportionately. So I tried adding the properties max-width: 100% and height: auto which makes images scale proportionately. But the following occurs:
Only the widest image will scale proportionately at all points when resizing the browser window as you make it smaller; the others will remain static until the point where the browser window is equal to the image’s width as defined by the indicated width and height properties.
Images center in the resized browser window as long as it is 1732 px wide (the width of the widest image and of the “stage” id which contains the images) or greater.
Is there a way to make all of the images scale smaller at all browser sizes?
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
#stage {
margin: 1em auto;
width: 1732px;
height: 1080px;
}
#stage img {
position: absolute;
max-width: 100%;
height: auto;
}
#stage img {
padding: 10px;
border: ;
background: #fff;
}
#stage img:nth-of-type(1) {
animation-name: fader;
animation-delay: 4s;
animation-duration: 1s;
z-index: 20;
}
#stage img:nth-of-type(2) {
z-index: 10;
}
#stage img:nth-of-type(n+3) {
display: none;
}
#keyframes fader {
from { opacity: 1.0; }
to { opacity: 0.0; }
}
</style>
</head>
<div id="stage">
<img src="http://www.bartonlewisfilm.com/cf_spring_&_thompson_east_v_1080.jpg" width="1394" height="1080">
<img src="http://www.bartonlewisfilm.com/cf_suffolk_btw_rivington_&_stanton_v_1080.jpg" width="1732" height="1080">
<img src="http://www.bartonlewisfilm.com/dr_chrystie_93_v_1080.jpg" width="1165" height="1080">
<img src="http://www.bartonlewisfilm.com/cf_franklin_&_w_bway_v_1080.jpg" width="726" height="1080">
</div>
<script type="text/javascript">
// Original JavaScript code by Chirp Internet: www.chirp.com.au
// Please acknowledge use of this code by including this header.
window.addEventListener("DOMContentLoaded", function(e) {
var maxW = 0;
var maxH = 0;
var stage = document.getElementById("stage");
var fadeComplete = function(e) { stage.appendChild(arr[0]); };
var arr = stage.getElementsByTagName("img");
for(var i=0; i < arr.length; i++) {
if(arr[i].width > maxW) maxW = arr[i].width;
if(arr[i].height > maxH) maxH = arr[i].height;
}
for(var i=0; i < arr.length; i++) {
if(arr[i].width < maxW) {
arr[i].style.paddingLeft = 10 + (maxW - arr[i].width)/2 + "px";
arr[i].style.paddingRight = 10 + (maxW - arr[i].width)/2 + "px";
}
if(arr[i].height < maxH) {
arr[i].style.paddingTop = 10 + (maxH - arr[i].height)/2 + "px";
arr[i].style.paddingBottom = 10 + (maxH - arr[i].height)/2 + "px";
}
arr[i].addEventListener("animationend", fadeComplete, false);
}
}, false);
</script>
</html>
Just add max-width to #stage
#stage{
max-width: 100%;
}
and put !important to images max-width
#stage img {
max-width: 100% !important
}

How to change data visible range to % percent

I am using this for my header that changes in a one page scroll up and down page. I noticed that it's not responsive so i am asking you if you maybe know a way to make that responsive. Like changing the 0-690 into a percentage so that it will work on mobile and also on a tv screen.
HTML
<div class="header header-1" data-visible-range="0-690">Portfolio</div>
<div class="header header-2" data-visible-range="691-2100">Services</div>
<div class="header header-3" data-visible-range="2101-">Contact</div>
CSS
.header-1 {
background-color:dimgray;
display: block;
}
.header-2 {
background-color:dimgray;
}
.header-3 {
background-color:dimgray;
}
.header {
position: fixed;
top: 0;
left: 0;
height:8vmax;
width: 100%;
display: none;
visibility:hidden;
transition: visibility .4s, opacity .4s ease-in-out;opacity:0;
font-size:4vmax;padding:1.58vmax;color:white;
}
What if, instead of basing it off pixels, you just checked to see if an element hit the top of the page, and then changed the header?
We'll call these elements "triggers." See my code below for an example of how they work.
let updateHeader = () => {
let scrollTop = $(window).scrollTop(),
triggerTitle = "Hi";
$('.trigger').each((i, el) => {
let topPos = $(el).offset().top,
distance = topPos - scrollTop;
if (distance < 0)
triggerTitle = $(el).data('title');
});
$('header h2').text(triggerTitle);
}
$(window).scroll(updateHeader);
$(window).on('touchmove', updateHeader);
body {
margin: 0;
}
#container {
height: 1000px;
}
header {
width: 100%;
position: fixed;
top: 0;
background-color: red;
}
p {
margin: 200px 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<header><h2>Hi</h2></header>
<p class="trigger" data-title="section1">
trigger1
</p>
<p class="trigger" data-title="section2">
trigger2
</p>
<p class="trigger" data-title="section3">
trigger3
</p>
</div>
As you scroll down the page, each trigger hits the top of the page, and the text in the header will change to the the value of the latest trigger's data-title. You could position these triggers appropriately above each of your website's sections, so that, no matter what size the screen, the header should update at the right time. Here's a codepen.
EDIT
Try this JS instead for maximum compatibility (no es6 involved).
function updateHeader() {
var scrollTop = $(window).scrollTop(),
triggerTitle = "Hi";
$('.trigger').each(function(i, el) {
var topPos = $(el).offset().top,
distance = topPos - scrollTop;
if (distance < 0)
triggerTitle = $(el).data('title');
});
$('header h2').text(triggerTitle);
}
$(window).scroll(updateHeader);
$(window).on('touchmove', updateHeader);

CSS position elements on the outside of a circle [duplicate]

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