I have been playing with CSS trying to create a 3d box that you can select the face with vanilla javascript.
It is simply changing the className of the box divs and using the transition property to smoothly transition between locations.
here is a jsfiddle to show a working example http://jsfiddle.net/synthet1c/VdDmA/1/
It looks cool at the moment but it is not quite behaving the way I want... Does anyone know how I can keep the box solid when it is going through it's transition? currently if the face is going from 360deg to 90deg the face will rotate 270deg in the wrong direction. I understand why it's doing it but can't make a workaround for it.
I added all the browser prefixes but have only used it on firefox.
cheers for any advice,
Andrew
#right, #back, #left, #front
{
height: 150px;
width: 150px;
position: absolute;
border: 1px solid rgba(200,200,200,0.7);
background-color: rgba(0,0,255,0.5);
margin: 0px;
}
.right{
transform: rotateY(90deg) translatez(75px) translatex(-75px);
transition: all 4s;
}
.back{
transform: rotateY(180deg) translatez(0px) translatex(0px);
transition: all 4s;
}
.left{
transform: rotateY(270deg) translatez(75px) translatex(70px);
transition: all 4s;
}
.front{
transform: rotateY(0deg) translatez(150px) translatex(0px);
transition: all 4s;
}
var id = function(elem){
var theId = document.getElementById(elem);
return theId;
}
function button1(){
id('front').className = 'front';
id('right').className = 'right';
id('back').className = 'back';
id('left').className = 'left';
}
function button2(){
id('front').className = 'right';
id('right').className = 'back';
id('back').className = 'left';
id('left').className = 'front';
}
function button3(){
id('front').className = 'back';
id('right').className = 'left';
id('back').className = 'front';
id('left').className = 'right';
}
function button4(){
id('front').className = 'left';
id('right').className = 'front';
id('back').className = 'right';
id('left').className = 'back';
}
It's actually pretty simple. You just need to do a check to see whether the difference, let's call it dif, in absolute value between the angle you're currently at and the angle you want to get to is over 180° and if it is, you rotate by 360° minus dif in absolute value, the direction of the rotation being given by the sign of dif.
I've also made some changes to the CSS, and if you want to understand more about how you can create a realistic looking cube, you can check my (really) detailed answer to a similar question.
demo
HTML:
<div class='buttons'>
<button class='btn'>Front</button>
<button class='btn'>Left</button>
<button class='btn'>Back</button>
<button class='btn'>Right</button>
</div>
<div class='house'>
<div class='face front'>Front</div>
<div class='face back'>Back</div>
<div class='face right'>Right</div>
<div class='face left'>Left</div>
</div>
Relevant CSS:
body /* or parent of .house */ { perspective: 45em; }
.house { position: relative; transform-style: preserve-3d; transition: 1s; }
.house, .face { width: 10em; height: 10em; }
.face {
box-sizing: border-box;
position: absolute;
padding: 1em;
}
.front { transform: translateZ(5em); background: rgba(255, 165, 0, .75); }
.back {
transform: rotateY(180deg) translateZ(5em);
background: rgba(30, 144, 255, .75);
}
.right {
transform: rotateY(90deg) translateZ(5em);
background: rgba(220, 20, 60, .75);
}
.left {
transform: rotateY(-90deg) translateZ(5em);
background: rgba(127, 255, 0, .75);
}
JavaScript:
(function(){
var btnsEl = document.querySelector('.buttons'), currentAngle = 0;
btnsEl.addEventListener('click', function(e){
var b = e.target.innerHTML.toLowerCase(),
house = document.querySelector('.house'),
btns = {'front': 0, 'left': 90, 'back': 180, 'right': -90},
dif = btns[b] - currentAngle%360;
if(!e.target.classList.contains('btn') || dif === 0) return;
currentAngle += (Math.abs(dif) > 180) ?
(Math.abs(dif) - 360)*Math.abs(dif)/dif :
dif;
house.style['-webkit-transform'] = 'rotateY(' + currentAngle + 'deg)';
house.style['transform'] = 'rotateY(' + currentAngle + 'deg)';
}, false);
}());
Also, you can do a rotating 3D box with pure CSS. However, the > 180° rotation problem can only be solved with JavaScript.
pure CSS version of the demo
Related
The + button on this page of my portfolio site provides more context and information on videos. It's composed of a simple "plus"-shaped SVG that I made contained within a circle-shaped div (border-radius: 100%), and animates out an additional div upon click.
On CodePen, where I originally coded this, the entire button (constituting both divs) is clickable. When I copied the code over to Squarespace, I noticed the upper half of the button is no longer clickable.
Since it works as intented on CodePen, I don't think there's an issue with the code (some relevant portions below but all is more easily visible in the CodePen link.
// HTML snippet:
<body>
<div id="feed">
<section class="slide" id="bB1">
<div class="containerBox cBBefore">
<div class="textContainer">
<div class="textSubCont">
<div class="plusButton noSelect">
<div class="plusSpan inactivePlus"></div>
</div>
</div>
</div>
</div>
</section>
// CSS Snippet:
.plusButton {
position: absolute;
pointer-events: auto;
cursor: default;
box-sizing: content-box;
border-radius: 100%;
right: -1.333vmin;
top: -1.333vmin;
height: 5vmin;
width: 5vmin;
z-index: 6;
background: rgba(79, 76, 220, 0.75);
-webkit-filter: drop-shadow(
-0.25vmin 0.5vmin 0.5vmin rgba(0, 0, 0, 0.33)) !important;
filter: drop-shadow(-0.25vmin 0.5vmin 0.5vmin rgba(0, 0, 0, 0.33)) !important;
}
.plusSpan {
content: url(https://www.charliemoody.com/s/plus.svg);
width: 100%;
height: 100%;
transform-origin: center;
-webkit-transform-origin: center;
}
.activePlus {
-webkit-transform: rotate(-765deg);
transform: rotate(-765deg);
-webkit-transition: transform 1s cubic-bezier(0.5, 0, 0.25, 1);
transition: transform 1s cubic-bezier(0.5, 0, 0.25, 1);
}
.inactivePlus {
-webkit-transform: rotate(0);
transform: rotate(0);
-webkit-transition: transform 1s cubic-bezier(0.5, 0, 0.25, 1);
transition: transform 1s cubic-bezier(0.5, 0, 0.25, 1);
}
// JS snippets
function enableButtons() {
let allButtons = document.querySelectorAll(".plusButton");
let allButtonSpans = document.querySelectorAll(".plusSpan");
let allCBs = document.querySelectorAll(".containerBox");
for (let i = 0; i < allButtons.length; i++) {
allButtons[i].addEventListener("click", function classToggle() {
allButtonSpans[i].classList.toggle("inactivePlus");
allButtonSpans[i].classList.toggle("activePlus");
}
}
function pauseOnPlus() {
let plusButtons = document.querySelectorAll(".plusButton");
let vids = document.querySelectorAll(".vidElement");
for (let i = 0; i < plusButtons.length; i++) {
plusButtons[i].addEventListener("click", function (event) {
if (vids[i].paused) vids[i].play();
else vids[i].pause();
});
}
}
function loadTheRest() {
enableButtons();
pauseOnPlus();
}
window.addEventListener("load", loadTheRest);
Nevermind!! Easy fix. Just needed to add "box-sizing: content-box;" to .plusSpan in the CSS.
Goal
Make the glare layer on all visible children like a clip-path
––––––––––––
Heads Up
Child elements can be any shape with any animation
Child elements can be any svg shape with any kind of animation attached to it
So glare must be automatically dynamic and conforming
––––––––––––
What I've Done
I create an apple TV effect…
But the glare only works as a box on top of other boxes.
The glare does not conform to other shapes
Example Below
––––––––––––
What I Can't Use
Canvas - No Canvas Please - I'm not familiar with it
Clip-Path - Because child elements can be anything overflowing outside of the glare
––––––––––––
What I'm looking for
Some kind of magical CSS line of code that makes the glare layer conform to all elements under it… like a normal glare would work.
Is this possible?
Is there some way Javascript can glare it automatically?
Is there some kind of mix-blend-mode I can use to make the glare just work?
Or is this something that is just impossible?
Glare should not look like a box
––––––––––––
What I tried
I tried to scale the glare layer to scale(1.1) and use some mix-blend-mode
But I couldn't figure out how to make it work.
appleTV();
function appleTV(){
appleTVComponents = 0;
function rotateX(n) {return ' rotateX('+n+'deg)'}
function rotateY(n) {return ' rotateY('+n+'deg)'}
function translateX(n) {return ' translateX('+n+'px)'}
function translateY(n) {return ' translateY('+n+'px)'}
function perspective(n) {return 'perspective('+n+'px)'}
function scale(n) {return ' scale3d('+n+','+n+','+n+')'}
function section(s='',e) {e=document.createElement('section');e.className='appletv_'+s;return e;}
function getWidth(e) {return e.clientWidth || e.offsetWidth || e.scrollWidth}
function setPerspective(e) {e.style.transform = perspective(getWidth(e)*3);}
function preventScroll(state) {if(supportsTouch){win.preventScroll=state||false;}}
function preventDefault(e) {if (supportsTouch&&win.preventScroll){e.preventDefault();}}
function isTouchScreen() {return 'ontouchstart' in window || navigator.msMaxTouchPoints}
function child(e) {return e.firstChild;}
function children(e) {return [...e.children]}
let body = document.body,
win = window,
imgs = document.querySelectorAll('.appletv'),
totalImgs = imgs.length,
supportsTouch = isTouchScreen(),
move = 'mousemove',
start = 'mouseenter',
end = 'mouseleave';
if(supportsTouch){move='touchmove'; start='touchstart'; end='touchend';}
if(totalImgs <= 0){return;}
for(var l=0;l<totalImgs;l++){
var thisImg = imgs[l],
layerElems = [...thisImg.querySelectorAll('.appletv_layer')];
if(!layerElems.length){continue;}
while(thisImg.firstChild) {thisImg.removeChild(thisImg.firstChild);}
var containerHTML = section(''),
shineHTML = section('gloss'),
shadowHTML = section('shadow'),
layersHTML = section('layer'),
layers = [];
thisImg.id = 'appletv_'+(++appleTVComponents);
layerElems.forEach((e,i)=>{
let layer_ = section('rendered_layer')
layer = section(''),
img = e.getAttribute('data-img');
layer_.setAttribute('data-layer',i);
[...e.children].forEach(c=>{layer.appendChild(c)})
if (img) {layer.style.backgroundImage = 'url('+img+')';}
layer_.appendChild(layer);
layersHTML.appendChild(layer_);
layers.push(layer);
});
[shadowHTML,layersHTML,shineHTML].forEach(e=>{containerHTML.appendChild(e)});
thisImg.appendChild(containerHTML);
var w = getWidth(thisImg);
setPerspective(thisImg)
preventScroll();
(function enableMovements(_thisImg,_layers,_totalLayers,_shine) {
thisImg.addEventListener(move, e=>{processMovement(e,supportsTouch,_thisImg,_layers,_totalLayers,_shine);});
thisImg.addEventListener(start, e=>{processEnter(_thisImg);});
thisImg.addEventListener(end, e=>{processExit(_thisImg,_layers,_totalLayers,_shine);});
})(thisImg,layers,layerElems.length,shineHTML);
};
function processMovement(e, touchEnabled, elem, layers, totalLayers, shine){
preventDefault(e)
let bdst = body.scrollTop,
bdsl = body.scrollLeft,
pageX = (touchEnabled)? e.touches[0].pageX : e.pageX,
pageY = (touchEnabled)? e.touches[0].pageY : e.pageY,
offsets = elem.getBoundingClientRect(),
w = elem.clientWidth || elem.offsetWidth || elem.scrollWidth, // width
h = elem.clientHeight || elem.offsetHeight || elem.scrollHeight, // height
wMultiple = 320/w,
offsetX = 0.52 - (pageX - offsets.left - bdsl)/w, //cursor position X
offsetY = 0.52 - (pageY - offsets.top - bdst)/h, //cursor position Y
dy = (pageY - offsets.top - bdst) - h / 2, //#h/2 = center of container
dx = (pageX - offsets.left - bdsl) - w / 2, //#w/2 = center of container
yRotate = (offsetX - dx)*(0.07 * wMultiple), //rotation for container Y
xRotate = (dy - offsetY)*(0.1 * wMultiple), //rotation for container X
imgCSS = rotateX(xRotate)+rotateY(yRotate), //img transform
arad = Math.atan2(dy, dx), //angle between cursor and center of container in RAD
angle = arad * 180 / Math.PI - 90; //convert rad in degrees
if (angle < 0) {angle = angle + 360;}
if(elem.firstChild.className.indexOf(' over') != -1){imgCSS += scale(1.07);}
elem.firstChild.style.transform = imgCSS;
shine.style.background = 'linear-gradient(' + angle + 'deg, rgba(255,255,255,' + (pageY - offsets.top - bdst)/h * 0.4 + ') 0%,rgba(255,255,255,0) 80%)';
shine.style.transform = translateX((offsetX * totalLayers) - 0.1)+translateY((offsetY * totalLayers) - 0.1);
var revNum = totalLayers;
for(var ly=0;ly<totalLayers;ly++){
layers[ly].style.transform = translateX((offsetX * revNum) * ((ly * 2.5) / wMultiple))+translateX((offsetY * totalLayers) * ((ly * 2.5) / wMultiple));
revNum--;
}
}
function processEnter(e){preventScroll(true);setPerspective(e);child(e)&&child(e).classList.add('over');}
function processExit(elem, layers, totalLayers, shine){preventScroll();
child(elem).classList.remove('over')
child(elem).style.transform = '';
shine.style = '';
layers.forEach(e=>{e.style.transform = ''})
}
}
body,
html {
height: 100%;
min-height: 100%;
}
body {background: linear-gradient(to bottom, #f6f7fc 0%, #d5e1e8 40%);}
.center{
position: absolute;
left: 50%;
margin: 10px auto;
transform: translateX(-50%);
}
.appletv {
position: relative !important;
margin: 0 auto !important;
display: inline-block;
width: 300px;
height: 150px;
border-radius: 5px;
transform-style: preserve-3d;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
cursor: pointer;
backface-visibility: hidden;
}
.appletv.depressed {
margin-top: 25px;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.4);
}
.appletv_ {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
transition: all 0.2s ease-out;
background: teal;
}
.appletv_container.over {z-index: 1;}
.appletv_container.over .appletv_shadow {box-shadow: 0 45px 100px rgba(14, 21, 47, 0.4), 0 16px 40px rgba(14, 21, 47, 0.4);}
.appletv_layer {
position: relative;
width: 100%;
height: 100%;
border-radius: 5px;
/*overflow: hidden;*/
transform-style: preserve-3d;
}
.appletv_rendered_layer {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
overflow: hidden;
border-radius: 5px;
transition: all 0.1s ease-out;
transform-style: preserve-3d;
}
.appletv_rendered_layer > :first-child {
position: absolute;
width: 104%;
height: 104%;
top: -2%;
left: -2%;
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
background-size: cover;
transition: all 0.1s ease-out;
}
.appletv_shadow {
position: absolute;
top: 5%;
left: 5%;
width: 90%;
height: 90%;
transition: all 0.2s ease-out;
box-shadow: 0 8px 30px rgba(14, 21, 47, 0.6);
}
.appletv_gloss {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 5px;
/*display: none !important;*/
background: linear-gradient(135deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0) 40%);
}
[data-layer="1"] {overflow: visible !important;}
[data-layer="1"] > section > section {
position: absolute;
background: rgb(50, 141, 210);
width: 60px;
height: 60px;
border-radius: 10px;
}
[data-layer="1"] > section > section:first-child {
left: -30px;
top: -10px;
}
[data-layer="1"] > section > section:last-child {
right: -20px;
top: 50px;
}
#keyframes rotate {
0% {transform: rotate(0);}
100% {transform: rotate(359deg);}
}
.appletv_gloss {
/*display: none;*/
background-blend-mode: multiply;
}
.appletv [data-layer="1"] {
transform: scale(0.5);
transition: .3s ease-in-out 0s;
}
.appletv:hover [data-layer="1"] {
transform: scale(1);
}
.appletv:hover [data-layer="1"] > section > section {
animation: rotate 10s linear 0s infinite;
}
.appletv:hover [data-layer="1"] > section > section:last-child {
animation: rotate 25s linear 0s infinite;
}
#hover {
font-size: 30px;
position: absolute;
top: 37%;
text-align: center;
width: 100%;
color: white;
text-shadow: 0 2px 2px rgba(0,0,0,0.3) ;
}
<html>
<body>
<section class="center">
<section class="appletv">
<section class="appletv appletv_layer" data-img="https://source.unsplash.com/random">
<section id="hover">Hover Corners</section>
</section>
<section class="appletv appletv_layer">
<section></section>
<section></section>
</section>
</section>
</section>
</body>
</html>
The question is almost the same as this: how to rotate the shadow effect with CSS?
But my question is a bit more complicated: i use "filter: drop-shadow" because object that i want to have shadow effect is composite - it consists of two primitive figures.
I achieved the desired effect with JS - just rotating the main object and then calculating drop-shadow direction. But the shadow blinks on rerendering, it is visible at least in Chrome.
(function() {
const RAD_TO_DEG = 180/Math.PI,
DEG_TO_RAD = Math.PI/180;
var arrow = document.getElementsByClassName('arrow')[0],
arrow_shadow_color = 'rgba(50,50,50,0.25)',
previous_x = 0,
previous_y = 0,
shadow_angle = -45,
shadow_blur_radius = 5,
shadow_offset = 15,
shadow_string_right = 'px ' + shadow_blur_radius + 'px ' + arrow_shadow_color + ')',
amount_of_attempts_to_skip = 10,
n = 0;
dropShadow(180);
document.addEventListener('mousemove', mouseMove);
function mouseMove(e) {
n++;
if (n%amount_of_attempts_to_skip === 0) {
var angle = Math.atan2( previous_y - e.pageY, e.pageX - previous_x ) * RAD_TO_DEG;
arrow.style.transform = 'rotate(' + (180 - ~~angle) + 'deg)';
dropShadow(angle);
previous_x = e.pageX;
previous_y = e.pageY;
}
}
function dropShadow(angle) {
angle = 180 - shadow_angle + angle;
var x = ( shadow_offset * Math.cos( angle * DEG_TO_RAD) ).toFixed(2),
y = ( shadow_offset * Math.sin( angle * DEG_TO_RAD) ).toFixed(2);
arrow.style.filter = 'drop-shadow(' + x + 'px ' + y + shadow_string_right;
}
})();
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
}
* {
margin: 0;
border: 0;
padding: 0;
position: relative;
box-sizing: inherit;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-100%, -50%);
}
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.15s ease;
}
.arrow:before {
content: "";
position: absolute;
border-bottom: 15px solid transparent;
border-right: 20px solid #2ECC40;
border-top: 15px solid transparent;
height: 0px;
width: 0px;
margin-left: -20px;
margin-top: -5px;
}
<div class="container"><div class="arrow"></div></div>
So the question is: is it possible to create a shadow effect for a composite object with CSS and then rotate it so that it keeps the absolute angle with CSS?
Or maybe at least with JS but some other way but manually setting x and y filter offsets.
UPD: i just realized that there is just no need to dynamically apply drop-shadow style - it can be applied to a container: there will be no rerendering flashes, no need to apply some techniques to smoothen the shadow movement, no need to manually calculate shadow offset, that's it. I answered my own question 'cuz it was silly.
I just realized that there is just no need to dynamically apply drop-shadow style - it can be applied to a container: there will be no rerendering flashes, no need to apply some techniques to smoothen the shadow movement, no need to manually calculate shadow offset, that's it. All of these will be rendered automatically.
So the answer for "is it possible to create a shadow effect for a composite object with CSS and then rotate it so that it keeps the absolute angle with CSS?" is Yes, it is possible: just apply drop-shadow filter to the container of the element that you want to have a shadow effect.
Stackoverflow, sorry for asking silly questions.
Shadow blinking is out of bug. I fixed your thing at my CodePen and below. Your project's arrow will get dynamic shadow with only CSS if you create pseudo element which will move with cursor.
That flickering of the shadow of 3D objects upon cursor move is browser specific long known CSS related kind of bug with fixes available everywhere. You only needed to know that matter. You can search StackOverflow and perform web search now. Two ways has minor difference in CSS. But both actually works. I have not changed your javascript.
You can read/see W3C docs, CSS tricks's this, CSS trick's this,W3 School and this code pen for CSS pseudo element drag-able drop shadow.
For your case I modified this :
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.01s ease;
transform-style: preserve-3d;
-webkit-transform: rotateY(60deg);
-webkit-transform-style: preserve-3d;
transform: rotateY(60deg);
(function() {
const RAD_TO_DEG = 180/Math.PI,
DEG_TO_RAD = Math.PI/180;
var arrow = document.getElementsByClassName('arrow')[0],
arrow_shadow_color = 'rgba(50,50,50,0.25)',
previous_x = 0,
previous_y = 0,
shadow_angle = -45,
shadow_blur_radius = 5,
shadow_offset = 15,
shadow_string_right = 'px ' + shadow_blur_radius + 'px ' + arrow_shadow_color + ')',
amount_of_attempts_to_skip = 10,
n = 0;
dropShadow(180);
document.addEventListener('mousemove', mouseMove);
function mouseMove(e) {
n++;
if (n%amount_of_attempts_to_skip === 0) {
var angle = Math.atan2( previous_y - e.pageY, e.pageX - previous_x ) * RAD_TO_DEG;
arrow.style.transform = 'rotate(' + (180 - ~~angle) + 'deg)';
dropShadow(angle);
previous_x = e.pageX;
previous_y = e.pageY;
}
}
function dropShadow(angle) {
angle = 180 - shadow_angle + angle;
var x = ( shadow_offset * Math.cos( angle * DEG_TO_RAD) ).toFixed(2),
y = ( shadow_offset * Math.sin( angle * DEG_TO_RAD) ).toFixed(2);
arrow.style.filter = 'drop-shadow(' + x + 'px ' + y + shadow_string_right;
}
})();
html, body {
width: 100%;
height: 100%;
box-sizing: border-box;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
* {
margin: 0;
border: 0;
padding: 0;
position: relative;
box-sizing: inherit;
transform-style: preserve-3d;
}
.container {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-100%, -50%);
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
}
.arrow {
width: 75px;
height: 20px;
background: #2ECC40;
transform-origin: right;
transition: all 0.01s ease;
transform-style: preserve-3d;
-webkit-transform: rotateY(60deg);
-webkit-transform-style: preserve-3d;
transform: rotateY(60deg);
}
.arrow:before {
content: "";
position: absolute;
border-bottom: 15px solid transparent;
border-right: 20px solid #2ECC40;
border-top: 15px solid transparent;
height: 0px;
width: 0px;
margin-left: -20px;
margin-top: -5px;
}
<div class="container"><div class="arrow"></div></div>
Depends on what kind of solution you are looking for. If you need a lot of elements with shadows, it's better to use a prerendered image. Browser won't spend time calculating all the shadows and rotations for each element.
If you absolutely need a shadow on a composite object with CSS, use box-shadow. There is a hacky way to make a triangle with the shadow. It's much better and efficient to use an image though!
Here by rotating the wrapper element we rotate all of its children and automatically their box-shadow:
(matrix value is taken from the computed style)
<!doctype html>
<html>
<head>
<style>
#keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.arrow {
top: 150px;
left: 50px;
position: relative;
display: block;
width: 280px;
animation: rotate 5s infinite linear;
}
.arrow div {
display: inline-block;
position: absolute;
}
.arrow-body {
width: 251px;
height: 25px;
top: 16px;
background: green;
box-shadow: 1px 5px 0 0 black;
}
.arrow-head {
width: 0;
height: 0;
right: 0;
bottom: -84px;
box-sizing: border-box;
border: 1em solid black;
border-color: transparent transparent green green;
transform-origin: 0 0;
transform: rotate(225deg);
box-shadow: -5px 1px 0 0 black;
}
#log {
font-family: monospace;
}
</style>
<script>
setInterval(function(){
var a = document.getElementById("arrow");
var l = document.getElementById("log");
l.innerHTML = ".arrow { transform: " + window.getComputedStyle(a, null).getPropertyValue("transform") + " }";
}, 10);
</script>
</head>
<body>
<span id="log"></span>
<div class="arrow" id="arrow">
<div class="arrow-body"></div>
<div class="arrow-head"></div>
</div>
</body>
</html>
In the following codepen, the creator has made a material ripple effect. However there is an issue where if I add another div right next to the original the ripple will leak into it.
What should I do to change to code so that the ripple will only be contained in the div that it was activated on?
I have tried editing the JS so that the click function only activates for divs with the class ".rippleDiv" but that did not work either.
Link to codepen http://codepen.io/Ruddy/pen/09052b957d82a17bd6ca70ac6663dd6a
HTML
<div class="rippleDiv">Button</div>
<div>Button 2</div>
CSS
div {
width: 220px;
height: 120px;
background: #222;
color: #fff;
text-align: center;
line-height: 120px;
font-size: 40px;
}
/* Ripple */
.ripple {
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
position: absolute;
opacity: 1;
}
.rippleEffect {
animation: rippleDrop .6s linear;
}
#keyframes rippleDrop {
100% {
transform: scale(2);
opacity: 0;
}
}
JS
$(".rippleDiv").click(function (e) {
// Remove any old one
$(".ripple").remove();
// Setup
var posX = $(this).offset().left,
posY = $(this).offset().top,
buttonWidth = $(this).width(),
buttonHeight = $(this).height();
// Add the element
$(this).prepend("<span class='ripple'></span>");
// Make it round!
if(buttonWidth >= buttonHeight) {
buttonHeight = buttonWidth;
} else {
buttonWidth = buttonHeight;
}
// Get the center of the element
var x = e.pageX - posX - buttonWidth / 2;
var y = e.pageY - posY - buttonHeight / 2;
// Add the ripples CSS and start the animation
$(".ripple").css({
width: buttonWidth,
height: buttonHeight,
top: y + 'px',
left: x + 'px'
}).addClass("rippleEffect");
});
The basic answer is that the 'ripple' element needs to be contained inside a div that has overflow:hidden set.
However to get this right, a number of small changes need to be made so that both the original button content, as well as the ripple itself, are correctly positioned, mainly using divs with the correct positioning attributes set.
So - here are the changes I made to get this to work: http://codepen.io/kitr/pen/xgLQpM
HTML:
<div>Button</div>
<div>Button 2</div>
CSS:
div {
width: 220px;
height: 120px;
background: #222;
color: #fff;
text-align: center;
line-height: 120px;
font-size: 40px;
position:relative;
overflow:hidden;
}
/* Ripple */
.ripple {
width: 0;
height: 0;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
transform: scale(0);
opacity: 1;
}
.rippleEffect {
animation: rippleDrop .6s linear;
position: absolute;
}
#keyframes rippleDrop {
100% {
transform: scale(2);
opacity: 0;
}
}
Javascript:
$("div").click(function (e) {
// Remove any old one
$(".ripple").remove();
// Setup
var posX = $(this).offset().left,
posY = $(this).offset().top,
buttonWidth = $(this).width(),
buttonHeight = $(this).height();
// Add the element
$(this).append("<div class='ripple'></div>");
// Make it round!
if(buttonWidth >= buttonHeight) {
buttonHeight = buttonWidth;
} else {
buttonWidth = buttonHeight;
}
// Get the center of the element
var x = e.pageX - posX - buttonWidth / 2;
var y = e.pageY - posY - buttonHeight / 2;
// Add the ripples CSS and start the animation
$(".ripple").css({
width: buttonWidth,
height: buttonHeight,
top: y + 'px',
left: x + 'px'
}).addClass("rippleEffect");
});
I'm doing a 3d dice that rotates when you swipe its face. The problem is that it is acting really weird on some cases. For example, if you run the snippet and swipe left two times then swipe down, it does a crazy rotation...
Here is the code:
$(function() {
var X = 0,
Y = 0;
var hammertime = new Hammer($(".thirdDimension")[0], {domEvents: true});
hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
function rotate(what) {
switch (what) {
case "X":
$(".cube").css("transform", "rotateX(" + X + "deg) rotateY(" + Y +"deg)");
break;
case "Y":
$(".cube").css("transform", "rotateY(" + Y +"deg) rotateX(" + X + "deg)");
break;
}
$("#debug").html($("#debug").html() + $(".cube").attr("style") + "<br>").scrollTop($("#debug")[0].scrollHeight);
}
$(".thirdDimension").on("swipeleft", function(e){
Y -= 90;
rotate("Y");
});
$(".thirdDimension").on("swiperight", function(e){
Y += 90;
rotate("Y");
});
$(".thirdDimension").on("swipeup", function(e){
X += 90;
rotate("X");
});
$(".thirdDimension").on("swipedown", function(e){
X -= 90;
rotate("X");
});
});
* {
box-sizing: border-box;
}
html, body {
height: 100%;
font-family: sans-serif;
}
.tableContainer, .vcenter {
height: 100%;
width: 100%;
}
.tableContainer {
display: table;
}
.vcenter {
height: 100%;
display: table-cell;
vertical-align: middle;
}
.thirdDimension {
perspective: 500px;
perspective-origin: 50% 100px;
}
.cube {
margin: 0 auto;
position: relative;
width: 200px;
height: 200px;
transform-style: preserve-3d;
}
.cube div {
position: absolute;
width: 200px;
height: 200px;
box-shadow: inset 0 0 30px black;
font-size: 72pt;
padding-top: 35px;
text-align: center;
background-color: black;
}
.right {
transform: rotateY(-270deg) translateX(100px);
transform-origin: top right;
}
.left {
transform: rotateY(270deg) translateX(-100px);
transform-origin: center left;
}
.up {
transform: rotateX(90deg) translateY(-100px);
transform-origin: top center;
}
.down {
transform: rotateX(-90deg) translateY(100px);
transform-origin: bottom center;
}
.front {
transform: translateZ(100px);
}
.back {
transform: translateZ(-100px) rotateY(180deg);
}
.cube {
-webkit-transition: .3s all linear;
cursor: pointer;
}
.cube div:after {
display: block;
position: absolute;
width: 100px;
height: 100px;
content: "";
border: 1px solid;
}
.front:after {
top: 0;
right: 0;
background-color: green;
}
.back:after {
bottom: 0;
right: 0;
background-color: blue;
}
.right:after {
top: 0;
left: 0;
background-color: red;
}
.left:after {
bottom: 0;
left: 0;
background-color: DarkOrange;
}
.up:after {
right: 0;
bottom: 0;
background-color: white;
}
.down:after {
bottom: 0;
left: 0;
background-color: yellow;
}
#debug {
position: fixed;
background-color: cyan;
overflow: auto;
height: 50px;
width: 100%;
}
/*
.cube {
animation: linear 5s rotate infinite;
}
#-webkit-keyframes rotate {
0% {
transform: rotateX(0deg) rotateY(0deg);
}
100% {
transform: rotateX(360deg) rotateY(720deg);
}
}
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<div id='debug'></div>
<div class='tableContainer'>
<div class='vcenter'>
<div class='thirdDimension'>
<div class='cube'>
<div class='up'></div>
<div class='front'></div>
<div class='right'></div>
<div class='left'></div>
<div class='back'></div>
<div class='down'></div>
</div>
</div>
</div>
</div>
EDIT: This link: http://greensock.com/forums/topic/7811-3d-rotation-fixed-axsis/ says that the order of the rotations matter. I changed my code slightly but it keeps rotating unexpectedly...
First: Settings the transform different ways for either "X" or "Y" was causing the massive spinning. I changed them both to:
$(".cube").css("transform", "rotateX(" + X + "deg) rotateY(" + Y +"deg)");
If believe you wish for the cube to always spin in the direction of the swipe. Continue reading if so or if not , all you needed to change was the above code.
Second: When you spin the cube you are changing the orientation. The x-axis is never messed up but the y-axis and z-axis are switching without you really noticing.
Now you can to add rotateZ to the mix:
$(".cube").css("transform", "rotateX(" + X + "deg) rotateY(" + Y +"deg) rotateZ(" + Z + "deg)");
I have changed the code here in this CodePen to provide an example. I have not completed it because I have revealed the issue, but do not have the time to perfect it.
$(function() {
var X = 0,
Y = 0,
Z = 0;
var hammertime = new Hammer($(".thirdDimension")[0], {domEvents: true});
hammertime.get('swipe').set({ direction: Hammer.DIRECTION_ALL });
function rotate() {
$(".cube").css("transform", "rotateX(" + X + "deg) rotateY(" + Y +"deg) rotateZ(" + Z + "deg)");
$("#debug").html($("#debug").html() + $(".cube").attr("style") + "<br>").scrollTop($("#debug")[0].scrollHeight);
}
$(".thirdDimension").on("swipeleft", function(e){
//Y -= 90;
check(-90);
rotate();
});
$(".thirdDimension").on("swiperight", function(e){
//Y += 90;
check(90);
rotate();
});
$(".thirdDimension").on("swipeup", function(e){
X += 90;
rotate();
});
$(".thirdDimension").on("swipedown", function(e){
X -= 90;
rotate();
});
function check(num) {
var temp = Math.abs(X % 360);
switch (temp) {
case 0:
console.log(temp);
Y += num;
break;
case 90:
console.log(temp);
Z -= num
break;
case 180:
console.log(temp);
Y -= num;
break;
case 270:
console.log(temp);
Z -= num;
break;
}
}
});
You need to check the y-axis (Math.abs(Y % 360))) and possibly the z-axis and rotate either the y-axis and z-axis (+- 90) accordingly.
This mostly works, but needs fine tuning in determining the correct axis to rotate. Good luck.
Your problem is more how you have conceptualized rotation than any coding mistakes.
If you rotateY(90), then rotateX(90) - then by the time you get to rotateX(90), your X axis has rotated by 90 degrees, so it is no longer left to right, but into/out of screen. So your thing will look like it's doing a 2d rotate in that particular instance.
And to take it further, if the user now swipes left to right, they are actually swiping along the Z axis, and you need to rotate Z.
To fix this, you need to keep track of the rotation you have done and map the user interface to the cube in a more dynamic way. Swiping left to right doesn't always mean rotate Y, it means rotate along the axis that is currently oriented left to right.
I suggest you find a rubik's cube or similar, and label the sides per axis (e.g. Red side is X axis), simulate the swiping, and then that should make things more clear, as you'll see those axis move around the cube.