问题
I'm attempting to create a circular navigation with n
elements navigable by clicking the keyboard's right and left arrows. I use classes with specific CSS3 rotate
and translate
values and a little bit of javascript in order to change the classes to do so.

My problem is that the transition between elements with classes four
and five
, the one that translates between 360deg
and 30deg
, which goes in the opposite direction than I want it to. You can see the obvious unwanted behavior here in this jsFiddle
.one { transform: rotate(270deg) translate(200px); }
.two { transform: rotate(300deg) translate(200px); }
.three { transform: rotate(330deg) translate(200px); }
.four { transform: rotate(360deg) translate(200px); }<-- Problematic transition
.five { transform: rotate(30deg) translate(200px); }<-- Problematic transition
.six { transform: rotate(60deg) translate(200px); }
.seven { transform: rotate(90deg) translate(200px); }
.eight { transform: rotate(120deg) translate(200px); }
.nine { transform: rotate(150deg) translate(200px); }
.ten { transform: rotate(180deg) translate(200px); }
.eleven { transform: rotate(210deg) translate(200px); }
.twelve { transform: rotate(240deg) translate(200px); }
I would prefer a CSS fix for this, but I also attempted a javascript one which can be found here. I only attempted using the left arrow in this example. My approach with javascript is to obtain the current translated value in string form, strip the rotate
degree from it, and add or subtract 30 degrees from it depending on whether or not the right or left arrow is pressed. The current problem with this approach is that it does not give me a value for element.style.webkitTransform
(I'm using the Chrome), it's just empty for some reason. Also I'd prefer not to have to prefix everything in the javascript, thus the reason why I would like a CSS only fix
My attempt at the javascript (untested because the first line won't work for some reason):
var thisTransform = circleArray[i].style.webkitTransform;
if (thisTransform.indexOf("rotate(") >= 0) {
var newDeg = parseInt(thisTransform.split("rotate(")[1].substring(thisTransform.indexOf("deg"))) - 30;
circleArray[i].style.webkitTransform = "rotate(" + newDeg + "deg) translate(200px) !important;";
}
Side note: I use prefixfree.min.js in my project to prevent form having to manually input all of the browser prefixes
Is there a CSS only fix for my problem that I am unaware of? What is causing the error when attempting to obtain the translate
value using javascript?
回答1:
First of all, let me say that I think that probably the best solution would be just rotate the base circle; I am almost sure that the results would be better and easier.
But this is just an by side idea, let's go to the problem that you post.
I have done a workaroud to your problem creating a class that will move correctly the problematic classes. My idea is to provide an animation that does the movement ok:
.foura { -webkit-transform: rotate(360deg) translate(200px);
-webkit-animation: aclock .3s linear 1;
transform: rotate(360deg) translate(200px);
animation: aclock .3s linear 1;
transition: none;
}
@keyframes aclock {
0% {transform: rotate(30deg) translate(200px);}
100% {transform: rotate(0deg) translate(200px);}
}
The idea is that the movement from 30 deg to 360 deg, being done thru an animation, can be changed to go from 30 deg to 0 deg.
I provide another similar class for class five.
The remaining problem is that the script must set the element to class four or foura depending on the rotation sense, and the same for five and fivea.
This lead to that (somewhat messy) script:
document.onkeydown = function (e) {
e = e || window.event;
switch(e.which || e.keyCode) {
case 37:
for (var i = 0, j = circleArray.length; i < j; i++) {
var curClassList = circleArray[i].classList,
curClass = curClassList.toString().split(' ')[1];
baseClass = curClass;
if (baseClass == "fivea") {
baseClass = "five";
}
if (baseClass == "foura") {
baseClass = "four";
}
if(circleClassArray.indexOf(baseClass) - 1 >= 0)
{
var newClass = circleClassArray[circleClassArray.indexOf(baseClass) - 1];
if (newClass == "four") {
newClass = "foura";
}
curClassList.add(newClass)
curClassList.remove(curClass);
} else {
curClassList.add(circleClassArray[j - 1]);
curClassList.remove(curClass);
}
}
break;
case 39:
for (var i = 0, j = circleArray.length; i < j; i++) {
var curClassList = circleArray[i].classList,
curClass = curClassList.toString().split(' ')[1];
baseClass = curClass;
if (baseClass == "fivea") {
baseClass = "five";
}
if (baseClass == "foura") {
baseClass = "four";
}
if(circleClassArray.indexOf(baseClass) + 1 < j)
{
var newClass = circleClassArray[circleClassArray.indexOf(baseClass) + 1];
if (newClass == "five") {
newClass = "fivea";
}
curClassList.add(newClass)
curClassList.remove(curClass);
} else {
curClassList.add(circleClassArray[0]);
curClassList.remove(curClass);
}
}
break;
}
}
And this is the full CSS:
.circle-big {
position: relative;
height:500px;
width:500px;
background:red;
border-radius: 50% 50%;
margin: 10% 10%;
border:5px solid black;
}
.circle-inner {
border-radius: 50%;
width: 300px;
height: 300px;
border: 5px solid white;
background-color: black;
display: block;
position: absolute;
overflow: hidden;
top: 50%;
left: 50%;
margin-top:-155px;
margin-left:-155px;
}
.circle {
border-radius: 50%;
width: 70px;
height: 70px;
background-color: white;
display: block;
position: absolute;
overflow: hidden;
top: 50%;
left: 50%;
margin-top:-35px;
margin-left:-35px;
transition: all .3s linear;
}
.one { -webkit-transform: rotate(270deg) translate(200px);
transform: rotate(270deg) translate(200px);
background:blue; }
.two { -webkit-transform: rotate(300deg) translate(200px);
transform: rotate(300deg) translate(200px); }
.three { -webkit-transform: rotate(330deg) translate(200px);
transform: rotate(330deg) translate(200px);
}
.four { -webkit-transform: rotate(360deg) translate(200px);
transform: rotate(360deg) translate(200px);}
.foura { -webkit-transform: rotate(360deg) translate(200px);
-webkit-animation: aclock .3s linear 1;
transform: rotate(360deg) translate(200px);
animation: aclock .3s linear 1;
transition: none;
}
.five { -webkit-transform: rotate(30deg) translate(200px);
transform: rotate(30deg) translate(200px);}
.fivea { -webkit-transform: rotate(30deg) translate(200px);
-webkit-animation: clock .3s linear 1;
transform: rotate(30deg) translate(200px);
animation: clock .3s linear 1;
transition: none;
}
.six { -webkit-transform: rotate(60deg) translate(200px);
transform: rotate(60deg) translate(200px);}
.seven { -webkit-transform: rotate(90deg) translate(200px);
transform: rotate(90deg) translate(200px);}
.eight { -webkit-transform: rotate(120deg) translate(200px);
transform: rotate(120deg) translate(200px);}
.nine { -webkit-transform: rotate(150deg) translate(200px);
transform: rotate(150deg) translate(200px); }
.ten { -webkit-transform: rotate(180deg) translate(200px);
transform: rotate(180deg) translate(200px);}
.eleven { -webkit-transform: rotate(210deg) translate(200px);
transform: rotate(210deg) translate(200px);}
.twelve { -webkit-transform: rotate(240deg) translate(200px);
transform: rotate(240deg) translate(200px);}
@-webkit-keyframes clock {
0% {-webkit-transform: rotate(0deg) translate(200px);}
100% {-webkit-transform: rotate(30deg) translate(200px);}
}
@-webkit-keyframes aclock {
0% {-webkit-transform: rotate(30deg) translate(200px);}
100% {-webkit-transform: rotate(0deg) translate(200px);}
}
@keyframes clock {
0% {transform: rotate(0deg) translate(200px);}
100% {transform: rotate(30deg) translate(200px);}
}
@keyframes aclock {
0% {transform: rotate(30deg) translate(200px);}
100% {transform: rotate(0deg) translate(200px);}
}
updated fiddle
Following my first idea, I have added the following code to the script:
case 38:
angle = angle + 30;
var circleBig = document.getElementsByClassName("circle-big")[0];
var style = "rotate(" + angle + "deg)";
circleBig.style.webkitTransform = style;
break;
case 40:
angle = angle - 30;
var circleBig = document.getElementsByClassName("circle-big")[0];
var style = "rotate(" + angle + "deg)";
circleBig.style.webkitTransform = style;
break;
And added also transition to the big circle. Now I think it works ok (on up and down arrow !)
second version
Brand new version
For the new requirement (to keep the inner circles upright) I have redesigned most of the fiddle. I have adapted an idea that I saw from Lea Verou, to make the rotation without auxiliar divs. It consists in setting the transform to angle + translation + oposite angle. That makes the div go where you want, unrotated.
Once decided to go this way, the script has to modified the styles of the inner circles, and not the style of the base circle. To do that easily, I have stored in every element the angle of that particular element (in a data- property).
Brand New demo
The resulting HTML is similiar:
<div class="circle-big">
<div class="circle one" data-angle=270>1</div>
<div class="circle two" data-angle=300>2</div>
<div class="circle three" data-angle=330>3</div>
<div class="circle four" data-angle=0>4</div>
<div class="circle five" data-angle=30>5</div>
<div class="circle six" data-angle=60>6</div>
<div class="circle seven" data-angle=90>7</div>
<div class="circle eight" data-angle=120>8</div>
<div class="circle nine" data-angle=150>9</div>
<div class="circle ten" data-angle=180>10</div>
<div class="circle eleven" data-angle=210>11</div>
<div class="circle twelve" data-angle=240>12</div>
<div class="circle-inner"></div>
</div>
And the script is
var circleArray = document.getElementsByClassName("circle");
var angle = 0;
window.onload = chargearray;
function chargearray () {
for (var i = 0, j = circleArray.length; i < j; i++) {
var circle = circleArray[i];
var circleAngle = parseInt (circle.dataset.angle);
var totalAngle = angle + circleAngle
var style = "rotate(" + totalAngle + "deg) translate(200px)";
totalAngle = - totalAngle;
style = style + " rotate(" + totalAngle + "deg)"
circle.style.webkitTransform = style;
circle.style.Transform = style;
}
}
document.onkeydown = function (e) {
e = e || window.event;
switch(e.which || e.keyCode) {
case 37:
angle = angle + 30;
chargearray ();
break;
case 39:
angle = angle - 30;
chargearray ();
break;
}
}
来源:https://stackoverflow.com/questions/18541324/multiple-elements-moving-circularly-smooth-transition