Javascript rotation animation from a rotated position [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I want to create a hand hour. The start position is 0 deg. If i click a button, the hand hour rotate to X deg with animation, and stay here, in X deg.Next time, I click the button, start a new rotation animation, and the hand hour rotate to X + Y deg from X deg, not the start 0 deg. How this operation is possible?

You could get the computed angle, increment it, and then update the CSS using JS.
const hand = document.querySelector('.hand'),
loop = document.querySelector('.loop'),
refreshRate = 60, tickAngle = 3;
let loopId = null;
const startStop = (e) => {
if (loopId) {
clearInterval(loopId); loopId = null;
} else {
loopId = setInterval(tick, 1000 / refreshRate);
}
loop.textContent = loopId ? 'Stop' : 'Start';
};
const tick = () => {
const degrees = (getCurrentAngle(hand) + tickAngle) % 360;
hand.style.transform = `rotate(${degrees}deg)`;
};
// Adapted from:
// https://css-tricks.com/get-value-of-css-rotation-through-javascript/
const getCurrentAngle = (el) => {
const st = window.getComputedStyle(el, null),
tr = st.getPropertyValue('-webkit-transform') ||
st.getPropertyValue('-moz-transform') ||
st.getPropertyValue('-ms-transform') ||
st.getPropertyValue('-o-transform') ||
st.getPropertyValue('transform'),
[a, b] = tr.match(/^matrix\((.+)\)$/)[1].split(/,\s*/g).map(Number);
return Math.round(Math.atan2(b, a) * (180 / Math.PI));
};
loop.addEventListener('click', startStop);
.face {
position: relative;
border: 0.25em solid black;
border-radius: 50%;
width: 8em;
height: 8em;
}
.hand {
position: absolute;
width: 0.25em;
height: 3.5em;
background: red;
left: 4em;
top: 4em;
transform-origin: 0.125em 0; /* x = width / 2 */
transform: rotate(180deg); /* Start at 0th hour */
}
<div class="face">
<div class="hand">
</div>
</div>
<div>
<button class="loop">Start</button>
</div>

Related

Is there any handler to rotate an object? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
This seems to be possible in the mobile version of interact.js while I am looking for a solution that works also in standard desktop browser.
I've found one solution on this Blog by bhch
Here is the code:
/*
* Some code borrowed from: https://codepen.io/taye/pen/wrrxKb
*/
interact('.rotation-handle')
.draggable({
onstart: function(event) {
var box = event.target.parentElement;
var rect = box.getBoundingClientRect();
// store the center as the element has css `transform-origin: center center`
box.setAttribute('data-center-x', rect.left + rect.width / 2);
box.setAttribute('data-center-y', rect.top + rect.height / 2);
// get the angle of the element when the drag starts
box.setAttribute('data-angle', getDragAngle(event));
},
onmove: function(event) {
var box = event.target.parentElement;
var pos = {
x: parseFloat(box.getAttribute('data-x')) || 0,
y: parseFloat(box.getAttribute('data-y')) || 0
};
var angle = getDragAngle(event);
// update transform style on dragmove
box.style.transform = 'translate(' + pos.x + 'px, ' + pos.y + 'px) rotate(' + angle + 'rad' + ')';
},
onend: function(event) {
var box = event.target.parentElement;
// save the angle on dragend
box.setAttribute('data-angle', getDragAngle(event));
},
})
function getDragAngle(event) {
var box = event.target.parentElement;
var startAngle = parseFloat(box.getAttribute('data-angle')) || 0;
var center = {
x: parseFloat(box.getAttribute('data-center-x')) || 0,
y: parseFloat(box.getAttribute('data-center-y')) || 0
};
var angle = Math.atan2(center.y - event.clientY,
center.x - event.clientX);
return angle - startAngle;
}
body {
font-family: sans-serif;
}
.box {
width: 150px;
height: 100px;
position: relative;
background-color: #4be091;
border-radius: 6px;
}
.rotation-handle {
padding: 3px 4px;
display: table;
position: absolute;
left: 50%;
right: 50%;
bottom: -35px;
background-color: #ff1661;
border-radius: 10rem;
line-height: 1;
text-align: center;
font-weight: bold;
color: #fff;
cursor: move;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.2.9/interact.min.js"></script>
<p>
<strong>Rotate the box using the red handle.</strong>
</p>
<div class="box">
<div class="rotation-handle">&circlearrowright;</div>
</div>

Trying to get this smoother and more natural in behavior

My implementation,
http://kodhus.com/kodnest/land/PpNFTgp
I am curious, as I am not able for some reason to figure this out, how to get my JavaScript to make my slider behave more natural and smoother, if someone knows, how to, or can make this, feel free. I'd be happy to understand.
JavaScript:
const thumb = document.querySelector('.thumb');
const thumbIndicator = document.querySelector('.thumb .thumb-indicator');
const sliderContainer = document.querySelector('.slider-container');
const trackProgress = document.querySelector('.track-progress');
const sliderContainerStart = sliderContainer.offsetLeft;
const sliderContainerWidth = sliderContainer.offsetWidth;
var translate;
var dragging = false;
var percentage = 14;
document.addEventListener('mousedown', function(e) {
if (e.target.classList.contains('thumb-indicator')) {
dragging = true;
thumbIndicator.classList.add('focus');
}
});
document.addEventListener('mousemove', function(e) {
if (dragging) {
console.log('moving', e)
if (e.clientX < sliderContainerStart) {
translate = 0;
} else if (e.clientX > sliderContainerWidth + sliderContainerStart) {
translate = sliderContainerWidth;
} else {
translate = e.clientX - sliderContainer.offsetLeft;
}
thumb.style.transform = 'translate(-50%) translate(' + translate + 'px)';
trackProgress.style.transform = 'scaleX(' + translate / sliderContainerWidth + ')'
}
});
function setPercentage() {
thumb.style.transform = 'translate(-50%) translate(' + percentage/100 * sliderContainerWidth + 'px)';
trackProgress.style.transform = 'scaleX(' + percentage/100 + ')';
}
function init() {
setPercentage();
}
init();
document.addEventListener('mouseup', function(e) {
dragging = false;
thumbIndicator.classList.remove('focus');
});
EDIT: Is there a way to smoothly and naturally increment by one for every slow move?
Is it possible to make to behave as if, like when one clicks the progress bar so that it jumps there?
The kodhus site is very janky in my browser, so I can't tell if your code lacks responsiveness or whether it's the site itself. I feel that your code is a bit convoluted: translate and width / height are mixed unnecessarily; no need to use a dragging boolean when that information is always stored in the classlist. The following slider performs nicely, and has a few considerations I don't see in yours:
stopPropagation when clicking the .thumb element
drag stops if window loses focus
pointer-events: none; applied to every part of the slider but the .thumb element
let applySliderFeel = (slider, valueChangeCallback=()=>{}) => {
// Now `thumb`, `bar` and `slider` are the elements that concern us
let [ thumb, bar ] = [ '.thumb', '.bar' ].map(v => slider.querySelector(v));
let changed = amt => {
thumb.style.left = `${amt * 100}%`;
bar.style.width = `${amt * 100}%`;
valueChangeCallback(amt);
};
// Pressing down on `thumb` activates dragging
thumb.addEventListener('mousedown', evt => {
thumb.classList.add('active');
evt.preventDefault();
evt.stopPropagation();
});
// Releasing the mouse button (anywhere) deactivates dragging
document.addEventListener('mouseup', evt => thumb.classList.remove('active'));
// If the window loses focus dragging also stops - this can be a very
// nice quality of life improvement!
window.addEventListener('blur', evt => thumb.classList.remove('active'));
// Now we have to act when the mouse moves...
document.addEventListener('mousemove', evt => {
// If the drag isn't active do nothing!
if (!thumb.classList.contains('active')) return;
// Compute `xRelSlider`, which is the mouse position relative to the
// left side of the slider bar. Note that *client*X is compatible with
// getBounding*Client*Rect, and using these two values we can quickly
// get the relative x position.
let { width, left } = slider.getBoundingClientRect();
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Clamp `xRelSlider` between 0 and the slider's width
if (xRelSlider < 0) xRelSlider = 0;
if (xRelSlider > width) xRelSlider = width;
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
slider.addEventListener('mousedown', evt => {
let { width, left } = slider.getBoundingClientRect();
// Clicking the slider also activates a drag
thumb.classList.add('active');
// Consider mouse x, subtract left offset of slider, and subtract half
// the width of the thumb (so drags position the center of the thumb,
// not its left side):
let xRelSlider = evt.clientX - left - (thumb.getBoundingClientRect().width >> 1);
// Apply styling (using percents is more robust!)
changed(xRelSlider / width);
evt.preventDefault();
evt.stopPropagation();
});
changed(0);
};
let valElem = document.querySelector('.value');
applySliderFeel(document.querySelector('.slider'), amt => valElem.innerHTML = amt.toFixed(3));
.slider {
position: absolute;
width: 80%; height: 4px; background-color: rgba(0, 0, 0, 0.3);
left: 10%; top: 50%; margin-top: -2px;
}
.slider > .bar {
position: absolute;
left: 0; top: 0; width: 0; height: 100%;
background-color: #000;
pointer-events: none;
}
.slider > .thumb {
position: absolute;
width: 20px; height: 20px; background-color: #000; border-radius: 100%;
left: 0; top: 50%; margin-top: -10px;
}
.slider > .thumb.active {
box-shadow: 0 0 0 5px rgba(0, 0, 0, 0.5);
}
<div class="slider">
<div class="bar"></div>
<div class="thumb"></div>
</div>
<div class="value"></div>

Zoom with transform:matrix(): how to stick to the left edge (alter zoom center)? [duplicate]

This question already has answers here:
How to scale from left to right only
(2 answers)
How to scale from right to left in CSS?
(2 answers)
Closed 3 years ago.
My client wants to have zoom functionality (I found that this is easy to implement with transform: scale() but after that he wants to have the top-left edge of a node at the same position. Sadly in school instead of math I played to WarCraft III and Space Rangers (iconic Russian game, try it), so now I do not know what kind of formula I need :(
One zoom position:
Another zoom position: as you see at the left we have empty "margin":
Minimum reproducible example is here:
const p = document.querySelector('p');
for (let i = 0; i < 50; i++) {
p.innerText += ' ' + Math.random() + Math.random();
}
document.querySelector('input').onchange = event => {
p.style.setProperty('--zoom', event.target.value);
}
input {
z-index: 1;
position: absolute;
}
p {
transform: matrix(
calc(var(--zoom) / 100),
0,
0,
calc(var(--zoom) / 100),
/* Next two lines is not perfect, what I need to write here? */
calc(var(--zoom) * var(--zoom) / 65 - 140),
calc(var(--zoom) * var(--zoom) / 1000)
);
}
<input type='range' min='10' max='200'></input>
<p></p>
Simple: alter origin with transform-origin: 0 0;
Original origin:
Here for example origin set at 30px 30px:
Read more https://dev.opera.com/articles/understanding-the-css-transforms-matrix/#coordinates
const p = document.querySelector('p');
for (let i = 0; i < 50; i++) {
p.innerText += ' ' + Math.random() + Math.random();
}
document.querySelector('input').onchange = event => {
p.style.setProperty('--zoom', event.target.value);
}
input {
z-index: 1;
position: absolute;
}
p {
transform-origin: 0 0;
transform: matrix(
calc(var(--zoom) / 100),
0,
0,
calc(var(--zoom) / 100),
0,
0
);
}
<input type='range' min='10' max='200'></input>
<p></p>

Is there a way to translate/ animate divs/images along a curve path in javascript and css?

I have a list of images , which need to scrolled on left or right arrow key press. But the scrolling needs to along a curve like a parabolic path . Is there a way to do this in javascript and css? If not not what should i use for this?
for your next question, please provide examples of what you have tried, what has failed, what you have researched or found out, etc. In general, please read "How do I ask a good question?" and "How to create a Minimal, Complete, and Verifiable example".
Nonetheless, I wanted to see what I can code in a few minutes before leaving work so here is a little something that might send you along the right way:
const circle = document.querySelector('.circle');
const path = function(x) {
return 0.1 * (x * x); // or any other functon
}
let x = 0;
const updateCircle = function() {
const y = path(x); // get y from x
const speed = 5;
const translateX = speed * x + 'px';
const translatey = speed * y + 'px';
const transform = `transform: translate(${translateX}, -${translatey})`;
circle.setAttribute('style', transform);
}
document.addEventListener('keydown', function(event) {
let move = false; // set move to true if left or right arrow pressed
switch (event.key) {
case "ArrowLeft": // moving left means decreasing x values
x--;
move = true;
break;
case "ArrowRight": // moving right means increasing x values
x++;
move = true;
break;
}
if (move) { // let or right arrow pressed
window.requestAnimationFrame(updateCircle);
}
}, true);
.circle {
background: blue;
width: 50px;
height: 50px;
border-radius: 50%;
position: absolute;
top: calc(100% - 50px);
left: calc(50% - 25px);
transition: transform 170ms linear;
}
body::before {
position: absolute;
top: 1rem;
left: 50%;
transform: translateX(-50%);
display: block;
content: 'click here to focus the snippet';
color: lightgray;
border: 1px solid lightgray;
padding: 1rem;
}
<div class="circle"></div>

Javascript/CSS - animation duration in pixel per second

How can i set the duration of an transition/animation to pixel per second?
You see the two different wrappers, with a different total height depending on it's colored content. The total speed is the same, given from the css transition attribute, thats okay if you want several animations with the same duration. For a smoother look i want to set this transition/animation effect to pixel per second so it takes as long as many pixels there. More content = more pixel = longer animation.
How can i achieve this with vanilla javascript or even css?
var wrapper1 = document.getElementById('wrapper1');
var wrapper2 = document.getElementById('wrapper2');
var header1 = document.getElementById('header1');
var header2 = document.getElementById('header2');
var wrapper1CmputedHeight = wrapper1.scrollHeight;
var wrapper2CmputedHeight = wrapper2.scrollHeight;
header1.addEventListener('click', function() {
if (wrapper1.style.height === '60px') {
wrapper1.style.height = wrapper1CmputedHeight + 'px';
} else {
wrapper1.style.height = '60px';
}
})
header2.addEventListener('click', function() {
if (wrapper2.style.height === '60px') {
wrapper2.style.height = wrapper2CmputedHeight + 'px';
} else {
wrapper2.style.height = '60px';
}
})
#wrapper1,
#wrapper2 {
background: #fff;
border: 1px solid grey;
overflow: hidden;
transition: height .2s linear
}
#wrapper1 {
margin-bottom: 40px
}
#header1,
#header2 {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer
}
#content1 {
height: 20px;
background: blue
}
#content2 {
height: 600px;
background: green
}
<div id="wrapper1" style="height: 60px">
<div id="header1">
<span>header</span>
</div>
<div id="content1"></div>
</div>
<div id="wrapper2" style="height: 60px">
<div id="header2">
<span>header</span>
</div>
<div id="content2"></div>
</div>
The only way to do this with css transitions, is to dynamically calculate the duration of the transition using a little javascript. So, in your code, I would remove the duration for the transition rule in your css, i,e.
#wrapper1,
#wrapper2 {
background: #fff;
overflow: hidden;
transition: height linear
}
and I would instead set the duration in the click handler as follows:
header1.addEventListener('click', function () {
if(wrapper1.style.height === '60px') {
wrapper1.style.height = wrapper1CmputedHeight + 'px';
wrapper1.style.transitionDuration=(wrapper1CmputedHeight/100)+"s";
} else {
wrapper1.style.height = '60px';
}
})
So in this case, I've used a speed of 100px per second (this is the /100 part in the above code).
I found this example here but it seems to do the trick for you (after some tweaking). In this case it implements a quartic interpolation, however you could adjust this algorithm to linear / other if so desired.
//
// Animate
//
var btn1 = document.querySelector('.animate');
btn1.addEventListener('click', function() {
reset();
animate();
btn1.disabled = true;
});
//
// http://easings.net/#easeInOutQuart
// t: current time
// b: beginning value
// c: change in value
// d: duration
//
function easeInOutQuart(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
function reset() {
document.querySelector('.square').style.width = Math.floor((Math.random() * 500) + 1) + "px";
}
function animate() {
var rect = document.querySelector('.square');
var from = 0;
var to = window.getComputedStyle(rect, null).getPropertyValue("width").split('px')[0];
var duration = to * 10;
var start = new Date().getTime();
var timer = setInterval(function() {
var time = new Date().getTime() - start;
var width = easeInOutQuart(time, from, to - from, duration);
rect.style.width = width + "px";
if (time >= duration) {
clearInterval(timer);
btn1.disabled = false;
}
}, 1000 / 60);
rect.style.width = from;
}
reset();
.square {
position: relative;
display: block;
width: 30px;
height: 30px;
background-color: #f00;
}
<div class="square"></div>
<button class="animate">Animate</button>

Categories

Resources