Why is my 3d cube not rotating as expected? - javascript

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.

Related

Animating with Javascript

In this code, I was trying to move the yellow box (the small box) to the right, bottom, left, and top respectively in the red box (the big box) and I have wanted to move the yellow box unendly. I have moved the yellow box to the right and bottom but couldn't move to the left and top. I couldn't understand what the problem is. How should I write this code in Javascript? Could you help me, please? I have used Visual Studio Code.
window.onload = function(){
var posX = 0,posY =0, posZ=0;
var smallbox = document.getElementById("smallbox");
var time = setInterval(move,10);
function move(){
if(posX>=150){
if(posY>=150){
if(posZ>=150){
clearInterval(time);
}
else{
posZ++;
smallbox.style.right = posZ + "px";
}
}
else{
posY++;
smallbox.style.top = posY + "px";
}
}
else{
posX = posX+1;
smallbox.style.left = posX + "px";
}
}
}
#bigbox{
height: 200px;
width: 200px;
background-color: red;
position: relative;
}
#smallbox{
height: 50px;
width: 50px;
background-color: yellow;
position: absolute;
}
<div id="bigbox">
<div id="smallbox">
</div>
</div>
The reason that your animation is not working is that CSS alignments for objects must use "top" or "bottom" and "left" or "right" to align themselves. What you are doing is aligning horizontally using "left" and then trying to align horizontally using "right", or the same thing but around the other way.
What I would instead suggest is using code that essentially reads:
if at top-left, move right.
if at top-right, move down.
if at bottom-right, move left.
if at bottom-left, move up.
An example of this in action:
window.onload = function() {
var posX = 0,
posY = 0,
boxW = 200,
boxH = 200,
smallboxW = 50,
smallboxH = 50;
var smallbox = document.getElementById("smallbox");
var time = setInterval(move, 10);
function move() {
if (posY <= 0 && posX < boxW) {
// go right
posX++;
smallbox.style.left = posX + "px";
}
if (posX >= boxW - smallboxW && posY < boxH) {
// go down
posY++;
smallbox.style.top = posY + "px";
}
if (posY >= boxH - smallboxH && posX > 0) {
// go left
posX--;
smallbox.style.left = posX + "px";
}
if (posX <= 0 && posY > 0) {
// go up
posY--;
smallbox.style.top = posY + "px";
}
}
}
#bigbox {
height: 200px;
width: 200px;
background-color: red;
position: relative;
}
#smallbox {
height: 50px;
width: 50px;
background-color: yellow;
position: absolute;
}
<body>
<div id="bigbox">
<div id="smallbox">
</div>
</div>
</body>
Instead of managing all calculations yourself, use the power of CSS in javascript with Element.animate().
This will also allow you to pause and play whenever needed.
const smallbox = document.querySelector('#smallbox');
smallbox.animate([
// keyframes
{
transform: 'translate(150px, 0px)'
},
{
transform: 'translate(150px, 150px)'
},
{
transform: 'translate(0px, 150px)'
},
{
transform: 'translate(0px, 0px)'
},
{
transform: 'translate(150px, 0px)'
},
], {
// timing options
duration: 2000,
iterations: Infinity
});
#bigbox {
height: 200px;
width: 200px;
background-color: red;
position: relative;
}
#smallbox {
height: 50px;
width: 50px;
background-color: yellow;
position: absolute;
}
<div id="bigbox">
<div id="smallbox">
</div>
</div>
One alternative option is to use the CSS keyframes to animate.
#bigbox {
height: 200px;
width: 200px;
background-color: red;
position: relative;
}
#smallbox {
height: 50px;
width: 50px;
background-color: yellow;
position: relative;
animation: move-around 4s infinite linear;
}
#keyframes move-around {
0% {
left: 0;
top: 0;
transform: translate(0%, 0%);
}
25% {
left: 100%;
top: 0;
transform: translate(-100%, 0%);
}
50% {
left: 100%;
top: 100%;
transform: translate(-100%, -100%);
}
75% {
left: 0;
top: 100%;
transform: translate(0%, -100%);
}
100% {
left: 0;
top: 0;
transform: translate(0%, 0%);
}
}
<body>
<div id="bigbox">
<div id="smallbox">
</div>
</div>
</body>

Javascript - Get an arm to point at elements being hovered over

I'm trying to get a pivoted forearm to point at the links that are being hovered over.
It works on the actual website I'm making it on but the pointing isn't quite accurate (it's nearly there) - I think it's perhaps because the code is designed to pivot using the center of the image (ie an arrow) and I'm using CSS transform-origin: center left; to force it otherwise?
I've done a fair bit of research but I can't get past this last hurdle - How do I adjust the JS to make the pointing rotation accurate?
Here is the setup I'm using:
$(function() {
var img = $('.arrow');
// Store clock wise degrees of all elements
var clockwiseElemDegrees = {};
var currentArrowAngle = 0;
// Treat initial position of arrow as element 0
var prevElem = '0';
clockwiseElemDegrees['0'] = 0;
if (img.length > 0) {
var offset = img.offset();
var imgX = offset.left + (img.width() / 2);
var imgY = offset.top + (img.height() / 2);
// Get element degrees
$('.animation-trigger').each(function() {
var element = $(this);
var elementPosition = element.offset();
var elementX = elementPosition.left + (element.width() / 2);
var elementY = elementPosition.top + (element.height() / 2);
var radians = Math.atan2(elementY - imgY, elementX - imgX);
var degrees = radians * (180 / Math.PI);
clockwiseElemDegrees[element.attr('elem')] = (degrees < 0) ? (degrees + 360) : degrees;
});
$('.animation-trigger').mouseenter(function(event) {
// Check if arrow should be rotated clockwise
var clockwiseDegreesForNextElem = clockwiseElemDegrees[$(this).attr('elem')];
var clockwiseDegreesForPrevElem = clockwiseElemDegrees[prevElem];
if (clockwiseDegreesForNextElem < clockwiseDegreesForPrevElem)
clockwiseDegreesForNextElem += 360;
var clockwiseRotationRequired = clockwiseDegreesForNextElem - clockwiseDegreesForPrevElem;
if (clockwiseRotationRequired <= 180) {
// Do clockwise rotation
currentArrowAngle += clockwiseRotationRequired;
} else {
// Do anticlockwise rotation
currentArrowAngle -= (360 - clockwiseRotationRequired);
}
prevElem = $(this).attr('elem');
img.css('-moz-transform', 'rotate(' + currentArrowAngle + 'deg)')
.css('-webkit-transform', 'rotate(' + currentArrowAngle + 'deg)')
.css('-o-transform', 'rotate(' + currentArrowAngle + 'deg)')
.css('-ms-transform', 'rotate(' + currentArrowAngle + 'deg)');
});
}
});
.scriptybits {
width: 44%;
box-sizing: border-box;
display: inline-block;
}
.wholepage {
width: 100%;
height: 95vh;
position: relative;
}
.scriptcontainer {
margin-left: 5% !important;
}
.arrow {
position: absolute;
margin-top: 20vh;
margin-left: 4px;
width: 20vh;
z-index: 5;
-webkit-transition: all 400ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
-moz-transition: all 400ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
-o-transition: all 400ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
transition: all 400ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
transform-origin: center left;
}
.pageleft {
display: inline-block;
}
.pageright {
text-align: center;
width: 100%;
display: block;
position: relative;
height: 90vh;
}
.menucontainer {
display: block;
margin: auto;
text-align: center;
width: 60%;
position: absolute;
left: 43%;
top: 9vh;
}
.leaningman {
height: 86vh;
display: block;
position: absolute;
top: -5vh;
}
.animation-trigger {
border: 3px solid black;
border-radius: 10px;
padding: 10px 30px 10px 30px;
color: black;
font-size: 30px;
background-color: white;
display: block;
margin-bottom: 20px;
text-align: center;
width: fit-content;
margin-left: auto;
margin-right: auto;
}
<div class="wholepage">
<div class="scriptybits">
<div class="pageleft" style="float:left; width:100%;">
<div class="scriptcontainer" style="position:relative;">
<img src="https://i.imgur.com/qxhaZOc.png" class="arrow">
<img class="leaningman" src="https://i.imgur.com/L9zVucE.jpg">
</div>
</div>
<div class="pageright">
<div class="menucontainer">
Link 1
Link 2
Link 3
Link 4
Link 5
Link 6
Link 7
</div>
</div>
</div>
</div>
You are rotating the image from the center left, but you are calculating the rotation angle based on the position from the center both horizontally and vertically. Changing to
var imgX = offset.left
seems to improve it for me.

Rotate drop-shadow effect with CSS

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>

How to make my logo JS animation scale like image logo on website?

I spent a bunch of time creating this really cool Logo animation our my site! The logo animation i made is the same size as the logo image and I can apply it to the site easily.
The problem is the logo on my site is set to start scaling down once the screen width reaches a certain point. It's set with a max width but also set to scale to the size of its parent div. My animation does not do this... I realized I need to figure this out before applying my logo animation to my site. Since it does not act in the same way... And i really dont wana use css scaling and media queries because that would be a mess.
Here my animation.
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 10;
// The maximum velocity in each direction
var maxVelocity = 3;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = 400;
var canvasHeight = 400;
// Create an image object (only need one instance)
var imageObj = new Image();
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "http://www.freeiconspng.com/uploads/misc-cloud-smoke-element-png-by-dbszabo1-on-deviantart-19.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if(this.image){
this.context.drawImage(this.image, this.x-228, this.y-228);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= canvasHeight) {
this.yVelocity = -this.yVelocity;
this.y = canvasHeight;
}
// Check if has crossed the top edge
else if (this.y <= 0) {
this.yVelocity = -this.yVelocity;
this.y = 0;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image){
this.image = image;
}
}
// A function to generate a random number between 2 values
function generateRandom(min, max){
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for(var i=0; i < particleCount; ++i){
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(0, canvasHeight));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
}
}
else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// Clear the drawing surface and fill it with a black background
//context.fillStyle = "rgba(0, 0, 0, 0.5)";
//context.fillRect(0, 0, 400, 400);
context.clearRect(0,0,1014,611);
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
var deg = [0, 0, 0, 0];
rotate = function() {
for (var i = 0; i < 3; ++i) {
deg[i] += 180 * 3;
if (Math.random() > 8)
deg[i] += 180;
$('#flip' + i).css({
'-webkit-transform': 'rotateY(' + deg[i] + 'deg)',
'-o-transform': 'rotateY(' + deg[i] + 'deg)',
'transform': 'rotateY(' + deg[i] + 'deg)'
});
}
};
rotate();
setInterval(rotate, 8000);
.animateVSS{
width:282px;
height:283px;
position:relative;
margin-top:20px;
margin-left:100px;
}
.vsslogocover{
position:absolute;
z-index:2;
}
.blackdrop{
position:relative;
top:-189px;
margin-left:44px;
opacity:0;
}
#myCanvas{
position:relative;
background:black;
position:absolute;
z-index;4;
margin-top:-190px;
margin-left:-190px;
border-radius:100%;
}
.coin {
background-image: url("http://portalpacific.net/jsfid/vss-animated-front-logo1.png");
background-size: 100% 100%;
border-radius: 100%;
height: 182px;
position: relative;
width:185px;
-webkit-transition: 1.8s ease-in-out;
-o-transition: 1.8s ease-in-out;
transition: 1.8s ease-in-out;
-webkit-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
margin-top:50px;
margin-left:48px;
z-index:1;
background-color: black;
}
.coin:before {
background-color: black;
background-image: url("http://portalpacific.net/jsfid/vss22-animated-back-logo-185x1821.png");
background-size: 100% 100%;
content: '';
height: 182px;
left: 0;
position: absolute;
top: 0;
width: 185px;
margin-top:1px;
-webkit-transform: translateZ(-5px);
-o-transform: translateZ(-5px);
transform: translateZ(-5px);
border-radius:100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<div class="animateVSS">
<img class="vsslogocover" src="http://portalpacific.net/jsfid/VSS%20Bazooka%20Logo%20animation%20cover%20logo%20border.png" alt="Smiley face" width="282" height="283">
<span id="flip0" class="coin" style="display:inline-block;"></span>
<img class="blackdrop" src="http://portalpacific.net/jsfid/bg2.png" alt="Smiley face" width="192" height="192">
<canvas id="myCanvas" width="200" height="200"></canvas>
</div>
Here's a JSFiddle also.
Thanks for any help!
Code has been Updated.
It uses the canvas effect and it is responsive and uses css3 animation as well. I tried to make the animation show both front and back of coin at the start and end of the animation :)
Cheers!
https://jsfiddle.net/BenjaminGraham/rudngfq9/
// Create an array to store our particles
var particles = [];
// The amount of particles to render
var particleCount = 10;
// The maximum velocity in each direction
var maxVelocity = 3;
// The target frames per second (how often do we want to update / redraw the scene)
var targetFPS = 33;
// Set the dimensions of the canvas as variables so they can be used.
var canvasWidth = 400;
var canvasHeight = 400;
// Create an image object (only need one instance)
var imageObj = new Image();
// Once the image has been downloaded then set the image on all of the particles
imageObj.onload = function() {
particles.forEach(function(particle) {
particle.setImage(imageObj);
});
};
// Once the callback is arranged then set the source of the image
imageObj.src = "http://www.freeiconspng.com/uploads/misc-cloud-smoke-element-png-by-dbszabo1-on-deviantart-19.png";
// A function to create a particle object.
function Particle(context) {
// Set the initial x and y positions
this.x = 0;
this.y = 0;
// Set the initial velocity
this.xVelocity = 0;
this.yVelocity = 0;
// Set the radius
this.radius = 5;
// Store the context which will be used to draw the particle
this.context = context;
// The function to draw the particle on the canvas.
this.draw = function() {
// If an image is set draw it
if(this.image){
this.context.drawImage(this.image, this.x-228, this.y-228);
// If the image is being rendered do not draw the circle so break out of the draw function
return;
}
// Draw the circle as before, with the addition of using the position and the radius from this object.
};
// Update the particle.
this.update = function() {
// Update the position of the particle with the addition of the velocity.
this.x += this.xVelocity;
this.y += this.yVelocity;
// Check if has crossed the right edge
if (this.x >= canvasWidth) {
this.xVelocity = -this.xVelocity;
this.x = canvasWidth;
}
// Check if has crossed the left edge
else if (this.x <= 0) {
this.xVelocity = -this.xVelocity;
this.x = 0;
}
// Check if has crossed the bottom edge
if (this.y >= canvasHeight) {
this.yVelocity = -this.yVelocity;
this.y = canvasHeight;
}
// Check if has crossed the top edge
else if (this.y <= 0) {
this.yVelocity = -this.yVelocity;
this.y = 0;
}
};
// A function to set the position of the particle.
this.setPosition = function(x, y) {
this.x = x;
this.y = y;
};
// Function to set the velocity.
this.setVelocity = function(x, y) {
this.xVelocity = x;
this.yVelocity = y;
};
this.setImage = function(image){
this.image = image;
}
}
// A function to generate a random number between 2 values
function generateRandom(min, max){
return Math.random() * (max - min) + min;
}
// The canvas context if it is defined.
var context;
// Initialise the scene and set the context if possible
function init() {
var canvas = document.getElementById('myCanvas');
if (canvas.getContext) {
// Set the context variable so it can be re-used
context = canvas.getContext('2d');
// Create the particles and set their initial positions and velocities
for(var i=0; i < particleCount; ++i){
var particle = new Particle(context);
// Set the position to be inside the canvas bounds
particle.setPosition(generateRandom(0, canvasWidth), generateRandom(0, canvasHeight));
// Set the initial velocity to be either random and either negative or positive
particle.setVelocity(generateRandom(-maxVelocity, maxVelocity), generateRandom(-maxVelocity, maxVelocity));
particles.push(particle);
}
}
else {
alert("Please use a modern browser");
}
}
// The function to draw the scene
function draw() {
// Clear the drawing surface and fill it with a black background
//context.fillStyle = "rgba(0, 0, 0, 0.5)";
//context.fillRect(0, 0, 400, 400);
context.clearRect(0,0,1014,611);
// Go through all of the particles and draw them.
particles.forEach(function(particle) {
particle.draw();
});
}
// Update the scene
function update() {
particles.forEach(function(particle) {
particle.update();
});
}
// Initialize the scene
init();
// If the context is set then we can draw the scene (if not then the browser does not support canvas)
if (context) {
setInterval(function() {
// Update the scene befoe drawing
update();
// Draw the scene
draw();
}, 1000 / targetFPS);
}
var deg = [0, 0, 0, 0];
rotate = function() {
for (var i = 0; i < 3; ++i) {
deg[i] += 180 * 3;
if (Math.random() > 8)
deg[i] += 180;
$('#flip' + i).css({
'-webkit-transform': 'rotateY(' + deg[i] + 'deg)',
'-o-transform': 'rotateY(' + deg[i] + 'deg)',
'transform': 'rotateY(' + deg[i] + 'deg)'
});
}
};
rotate();
setInterval(rotate, 8000);
.animateVSS {
width: 282px;
height: 283px;
position: relative;
}
.vsslogocover {
position: absolute;
z-index: 2;
}
.blackdrop {
position: relative;
opacity: 0;
}
#myCanvas {
position: absolute;
background: black;
z-index;
4;
top: 17.5%;
left: 17.5%;
bottom: 0;
border-radius: 100%;
}
.coin {
background-image: url("http://portalpacific.net/jsfid/vss-animated-front-logo1.png");
background-size: 100% 100%;
border-radius: 100%;
height: 65%;
width: 65%;
top: 17.5%;
left: 17.5%;
position: relative;
-webkit-transition: 1.8s ease-in-out;
-o-transition: 1.8s ease-in-out;
transition: 1.8s ease-in-out;
-webkit-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
z-index: 1;
background-color: black;
}
.coin:before {
background-color: black;
background-image: url("http://portalpacific.net/jsfid/vss22-animated-back-logo-185x1821.png");
background-size: 100% 100%;
content: '';
height: 100%;
width: 100%;
left: 0%;
position: absolute;
top: 0%;
-webkit-transform: translateZ(-5px);
-o-transform: translateZ(-5px);
transform: translateZ(-5px);
border-radius: 100%;
}
#flip0 {
-webkit-animation-name: Logo;
animation-name: Logo;
-webkit-animation-duration: 5s;
animation-duration: 5s;
-webkit-animation-iteration-count: infinite;
/* Safari 4.0 - 8.0 */
animation-iteration-count: infinite;
}
#keyframes Logo {
0% {
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
}
25% {
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
}
50% {
transform: rotateY(900deg);
-webkit-transform: rotateY(900deg);
}
75% {
transform: rotateY(900deg);
-webkit-transform: rotateY(900deg);
}
100% {
transform: rotateY(0deg);
-webkit-transform: rotateY(0deg);
}
}
#media screen and (max-width:767px) {
.animateVSS {
width: 180px;
height: 180px;
}
}
#media screen and (min-width:768px) {
.animateVSS {
width: 280px;
height: 280px;
}
#myCanvas {
width: 180px;
height: 180px;
top: 17.5%;
left: 17.5%;
}
}
<div class="animateVSS">
<img class="vsslogocover" src="http://portalpacific.net/jsfid/VSS%20Bazooka%20Logo%20animation%20cover%20logo%20border.png" alt="Smiley face" width="100%" height="100%">
<span id="flip0" class="coin" style="display:inline-block;">
</span>
<img class="blackdrop" src="http://portalpacific.net/jsfid/bg2.png" alt="Smiley face" width="100%" height="100%">
<canvas id="myCanvas" width="120%" height="120%"></canvas>
</div>
Not exactly an expert here but I tried to mess around with your codes.
From what I see is that all the css parameters in the code are fixed pixels which makes the elements be not responsive to screen size.
You have to make the parent div fixed pixel (or responsive if you'd like) and then set all the child elements be responsive to the parent. This can be 100% of the parent's width.
Thus in this way when the screen size shrinks, you needn't worry about the elements getting disrupted. They will be all encapsulated in the parent div.
This is not exactly the perfect code you require but it will give you a good idea of what is happening here.
CSS
.animateVSS{
position:relative;
width:100px;
height:100px;
}
.vsslogocover{
position:absolute;
z-index:2;
width:100%;
}
.blackdrop{
position:absolute;
width:100%;
}
#myCanvas{
background:black;
position:absolute;
z-index;4;
width:100%;
height:100%;
border-radius:50%;
}
.coin {
background-image: url("http://portalpacific.net/jsfid/vss-animated-front-logo1.png");
background-size:contain;
border-radius: 50%;
position: absolute;
width:100%;
-webkit-transition: 1.8s ease-in-out;
-o-transition: 1.8s ease-in-out;
transition: 1.8s ease-in-out;
-webkit-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
z-index:1;
background-color: black;
}
.coin:before {
background-color: black;
background-image: url("http://portalpacific.net/jsfid/vss22-animated- back-logo-185x1821.png");
background-size: contain;
content: '';
height: 70px;
left: 0;
position: absolute;
top: 0;
width: 70px;
margin-top:15px;
margin-left:15px;
border-radius:100%;
}
HTML
<div class="animateVSS">
<img class="vsslogocover" src="http://portalpacific.net/jsfid/VSS%20Bazooka%20Logo%20animation%20cover%20logo%20border.png" alt="Smiley face">
<span id="flip0" class="coin" style="display:inline-block;"></span>
<img class="blackdrop" src="http://portalpacific.net/jsfid/bg2.png" alt="Smiley face">
<canvas id="myCanvas"></canvas>
</div>
I tweaked your css a bit to make use of percentage wherever possible, Just replace your CSS with this one and you will see that your logo is now responsive. You can tweak the percentage to make it wider or smaller to your liking:
rotate();
setInterval(rotate, 8000);
#myCanvas{
position:relative;
background:black;
position:absolute;
z-index;4;
margin-top:-190px;
margin-left:-190px;
border-radius:100%;
}
.animateVSS {
position: relative;
width: 20%;
max-width: 282px;
max-height: 283px;
padding-top: 20%;
display: inline-block;
}
.vsslogocover {
position: absolute;
z-index: 2;
width: 100%;
height: auto;
max-width: 282px;
max-height: 283px;
top: 0;
}
.coin {
height: 70%;
max-height: 182px;
width: 66%;
max-width: 185px;
margin-top: 16%;
margin-left: 17%;
top: 0;
position: absolute;
background-image: url(http://portalpacific.net/jsfid/vss-animated-front-logo1.png);
background-size: 100% 100%;
border-radius: 100%;
-o-transition: 1.8s ease-in-out;
transition: 1.8s ease-in-out;
-webkit-transform-style: preserve-3d;
-o-transform-style: preserve-3d;
transform-style: preserve-3d;
z-index: 1;
background-color: black;
}
.coin:before {
background-color: black;
background-image: url(http://portalpacific.net/jsfid/vss22-animated-back-logo-185x1821.png);
background-size: 100% 100%;
content: '';
left: 0;
position: absolute;
margin-top: 1px;
-webkit-transform: translateZ(-5px);
-o-transform: translateZ(-5px);
transform: translateZ(-5px);
border-radius: 100%;
height: 100%;
max-height: 182px;
width: 100%;
max-width: 185px;
top: 0;
}
.blackdrop {
position: absolute;
opacity: 0;
width: 100%;
height: auto;
max-width: 282px;
max-height: 283px;
top: 0;
}
#myCanvas {
background: black;
position: absolute;
margin-left: 17%;
border-radius: 100%;
top: 0;
width: 70%;
max-width: 200px;
max-height: 200px;
height: auto;
margin-top: 17%;
}
You're setting an explicit width and working with explicit pixel values and coordinates. That's going to make it tricky. Try working all the coordinates as a percentage of the width, and then you can dynamically detect the width (or set it outside the class) and everything should scale. It's not a quick fix, but it's not very complicated either.

How can I prevent this material ripple animation from leaking out into other div?

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");
});

Categories

Resources