Javascript animate Method Accumulation Problem - javascript

I am trying to do an animation example but there is a problem about accumulation. At first click on the button, the translate animation is done and the position of the element changes permanently. However, on second click it does the translate animation again but this time does not keep the last position. How to overcome this? I went through MDN document and applied the optional section but failed to complete this challenge. Regards,
https://jsfiddle.net/ja218pbr/19/
document.getElementsByTagName("button")[0].addEventListener("click", function() {
document.querySelectorAll("div")[0].animate([
{transform: 'translate(100%, 0)'}
], {
duration: 250,
composite: "accumulate",
iterationComposite: "accumulate",
fill: "forwards"
});
});

If I'm understanding this correctly, you want to be able to let the object slide again from the position it ended in earlier. To do this we can get the boundingClientRect each time the button is clicked and calculate the new translation distance by basically taking the width of the object and adding the left distance of the client rect, this will effectively allow the rectangle to keep moving from the distance it ended in before. Also I removed the composite property because it caused the rectangle to jump over the correct position.
document.getElementsByTagName("button")[0].addEventListener("click", function() {
const clientRect = document.querySelectorAll("div")[0].getBoundingClientRect();
const translationX = clientRect.width + clientRect.left;
document.querySelectorAll("div")[0].animate([{
transform: `translate(${translationX}px, 0)`
}], {
duration: 250,
iterationComposite: "accumulate",
fill: "forwards"
});
});
body {
margin: 0;
}
div {
position: relative;
height: 150px;
width: 150px;
background-color: yellow;
text-align: center;
line-height: 150px;
}
<div>Moving Object</div>
<button>Press</button>

Related

GSAP/CSS transform origin and glitchy hover effect/ SVG Regions

I am attempting to animate an svg doughnut circle with gsap. After much testing with code and layering, I am stumped with a glitchy hover effect (which I tried to resolve with pointer events) and the transform origin is only applied to a few of the expanded tabs. I am wondering if this might be that the tabs may have a different bounding box?
Comments added per request:
Side Note: I've tried applying fill-box to entire svg instead, I'm wondering if I need a parent layer thats an exact square so I can apply the transform origin for the child "expandtabs" to the center of that?
I assumed I needed to iterate through an array of both to have the tabs correspond. Unless the tabs were children of each other?
TLDR; Tabs are not scaling from center of circle, and glitchy hover effect
CodePen Example
.expandtab {
pointer-events: none;
transform: fill-box;
transform-origin: -15px 25%;
}
Javascript:
const subTabs = gsap.utils.toArray(".subtab");
const expandTabs = gsap.utils.toArray(".expandtab");
const tl = gsap.timeline({ defaults: { duration: .05, } });
tl.set(expandTabs, {
visibility: "hidden",
opacity: 0,
scale: 0,
});
subTabs.forEach((subTab, index) => {
let expandTab = expandTabs[index];
// Event Listener Hover on
subTabs[index].addEventListener("mouseover", (event) => {
console.log("you clicked region number " + index);
tl.to(expandTab, {
visibility: "visible",
opacity: 1,
scale: 1,
});
});
// Event Listener Hover off
subTabs[index].addEventListener("mouseout", (event) => {
console.log("you exited region number " + index);
tl.to(expandTab, {
opacity: 0,
scale: 0,
visibility: "hidden",
});
});
});
About the glitchy hover effect, the mouseenter and mouseleave will do the job better. mouseover is firering way to much...
For the "growing" effect, it is more complex. The transform-origin CSS property won't be enought. Any way, you will need different values for each five parts of the circle.
Additionnaly, you will need to adjust a transition to "fit" or "keep" the inner part of the circle in place. I suggest you to look at the fromTo method of GSAP. That will allow you to specify explicitely the starting and the landing coordinates.
Be patient! ;)

How to position an element under the cursor if its parent has "margin: 0 auto"?

I'm building a simple draggable functionality:
<div id="app">
<div id="i-plane">
<div
id="i-interact"
v-bind:style="position"
v-on:mousedown="handleDown"
v-on:mousemove="handleMove"
v-on:mouseout="handleUp">
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
field: {
x: 200,
y: 200
},
dragging: false
},
computed: {
position: function() {
return 'transform: translate(' + this.field.x + 'px,' + this.field.y + 'px)'
}
},
methods: {
handleUp: function(e) {
this.dragging = false
},
handleDown: function(e) {
this.dragging = true
},
handleMove: function(e) {
if (this.dragging) {
this.field.x = e.clientX
this.field.y = e.clientY
}
}
}
});
</script>
<style>
#i-plane {
background: red;
width: 400px;
height: 400px;
margin: 0 auto
}
#i-interact {
background: black;
width: 40px;
height: 40px;
}
</style>
If you click #i-interact the element is not positioned under the cursor but it flies to the right.
How to modify the code so #i-interact moves under the cursor?
Codepen: https://codepen.io/alexcheninfo/pen/XNLNvM
There are a couple of issues here. First, since you're using translate to move the element, you'll need to know its relative position. So, rather than specifying the position on the screen where the mouse is when it's moving, you'll have to specify how many pixels the mouse has moved relative to where it started on mouse down. (The other alternative would be to position the element absolutely, but I'm assuming you want to stick with translate.)
The other issue is that your mouse handlers are all listening to events on the element that you're moving. This will result in very unreliable behavior because if, for example, you move your mouse outside of the element by moving the mouse quickly, your movements will no longer be tracked.
See my fork of your CodePen. I have added an object to know the originating start coordinates and I've made the mouse event listeners document-wide and moved them into created():
created() {
document.addEventListener('mousedown', this.handleDown)
document.addEventListener('mouseup', this.handleUp)
document.addEventListener('mousemove', this.handleMove)
},
This works well enough in your specific example, but it is still not perfect because the start position is based on where you originally click into the element to move it, and also you may just want the mouse events tracked within a given container. To make it better you could upon mouseup actually move the element to the new position and reset the translate to zero, then on the next mousedown you could set the new start position.
As there are a lot of possible routes you could take here and I don't want to just guess at what your requirements are, so I'm providing just a basic working version of your example. Hopefully it suits your needs sufficiently!

Enyo JS Vertical Sliders

I am trying to code a vertical slider in enyo (Like a control on mixing desk). I was trying to avoid starting from scratch so I started tweaking the onyx.Slider class. I changed to styles from left to top and from width to height and with a few other tweaks, it's working. I'm now stuck on getting the slider to fill from bottom to top as at the minute it is vertical but it fills from the top down. Thanks in advance for any help.
Here are the code changes I have done:
in ProgressBar.js:
updateBarPosition: function(inPercent) {
this.$.bar.applyStyle("height", inPercent + "%");
},
in Slider.js (dividing by 64 is a temporary hack):
valueChanged: function() {
this.value = this.clampValue(this.min, this.max, this.value);
var p = this.calcPercent(this.value);
this.updateKnobPosition(p/64);
if (this.lockBar) {
this.setProgress(this.value);
}
},
updateKnobPosition: function(inPercent) {
this.$.knob.applyStyle("top", inPercent + "%");
},
calcKnobPosition: function(inEvent) {
var y = inEvent.clientY - this.hasNode().getBoundingClientRect().top;
return (y / this.getBounds().height) * (this.max - this.min) + this.min;
},
CSS:
/* ProgressBar.css */
.onyx-progress-bar {
margin: 8px;
height: 400px;
width: 8px;
border: 1px solid rgba(15, 15, 15, 0.2);
border-radius: 3px;
background: #b8b8b8 url(./../images/gradient-invert.png) repeat-x;
background-size: auto 100%;
}
.onyx-progress-bar-bar {
height: 100%;
border-radius: 3px;
background: #58abef url(./../images/gradient.png) repeat-x;
background-size: auto 100%;
}
Tom
There are a couple of approaches you could take. The most obvious (except for the fact it didn't occur to me first) is just to swap the background/gradient of the bar and the bar-bar. This will give you the appearance of filling from the bottom. I would recommend this.
The other method is what I did in this jsFiddle here: http://jsfiddle.net/RoySutton/b9PmA/ (Do ignore the doubled updateBarPosition function)
Instead of modifying those files directly, I derived from Slider and overrode the appropriate functions and added a new class for the vertical slider.
I changed the 'fill' to be absolutely positioned within the slider.
Now, your next problem is that value '0' is fully filled and '100' is fully empty. I handled that by modifying your calcKnobPosition to adjust from max and inverting the positioning logic as seen in this fiddle here: http://jsfiddle.net/RoySutton/b9PmA/2/
return this.max - (y / this.getBounds().height) * (this.max - this.min);

Two triangular clickable area within a square

Basically I want to split a square div diagonally in two resulting in two triangles.
Each triangle has to respond to the hover event.
This is what I have so far but the problem is: if you go from one corner of the div straight to the opposite corner it doesn't re-trigger the hover event since the event is applied to the div element and not the define triangle area within.
I'm open to any suggestions, I don't even mind if I need to approach the problem from a different angle all together. There's got to be an easier solution, at least I hope!
The HTML
<div class="day_box">
</div>
The CSS
html, body { margin: 0; }
.day_box, .upper_left_hover, .lower_right_hover, .full_day {
background: url(/images/corner-sprites.png);
border: 1px solid black;
width: 25px;
height: 25px;
float: left;
margin: 100px;
}
.upper_left_hover { background-position: 75px 0; }
.lower_right_hover { background-position: 50px 0; }
.full_day { background-position: 25px 0; }
The JS
$(".day_box").hover(function(event){
var offset = $(this).offset();
var h = $(this).height() + offset.top;
if((h - event.pageY)>(event.pageX - offset.left)) {
console.log("Upper left");
$(this).toggleClass("upper_left_hover");
} else {
console.log("Lower right");
$(this).toggleClass("lower_right_hover");
}
});
The Fiddle: http://jsfiddle.net/zsay6/
You can use the mousemove event like this (adding mouseout to remove both of the classes when you leave the square):
$(".day_box").mousemove(function(event){
var offset = $(this).offset();
var h = $(this).height() + offset.top;
if((h - event.pageY)>(event.pageX - offset.left)) {
console.log("Upper left");
$(this).removeClass("lower_right_hover");
$(this).addClass("upper_left_hover");
} else if ((h - event.pageY)<(event.pageX - offset.left)) {
console.log("Lower right");
$(this).removeClass("upper_left_hover");
$(this).addClass("lower_right_hover");
}
}).mouseout(function(event)
{
$(this).removeClass("lower_right_hover upper_left_hover");
});
http://jsfiddle.net/zsay6/14/
I altered your fiddle to produce the effect you wanted... and I didn't clean it up at all (was just fiddling... haha)
Using the right-triangle formula (here), I set the given style you set up in your original fiddle. It also throws up some values in a debugging div so you can see it in action a little more clearly.
You can also use HTML map areas for that purpose:
http://www.w3schools.com/tags/tag_map.asp
On hover, change the background of the element to which the usemap is applied.

jquery mouse move capture inaccuracy

I'm facing an strange problem.
I capture the mouse movements with:
var mmoves = [];
jQuery(document).mousemove(function(event) {
mmoves.push({x:event.pageX, y:event.pageY})
}
Then I attach a div to the page like:
$("body").append('<div id="mouseemul" style="padding:0; margin:0; color: red; background-color: blue; width: 1px; height: 1px;">*</div>');
and then try to playback the moves
It works ok on most pages but on some pages the playback starts ("*" initial position) some pixels to the right (x). The y is ok but the x is about 120px to the right. On other pages it is accurate. On the not accurate pages, when the mouse goes close the right scrollbar it goes beyond the right page border and produces a horizontal scrollbar.
I think this has to do with some css styling of the page being playback.
Does anybody has an idea what may be causing this ?
How could I get the actual offset (in case there is an offset for such pages) ?
Thanks a lot,
Hernan
--Edited--
It is obvious that the x displacement is due to the positioning of the main document. The first element gives a $.position() of 0,134 and if I SUBSTRACT that amount from the recorded data the playback is accurate. The problem is that this displacement does not happen in every page and I dont know how to figure out when the displacement occurs and when not (to correct it by substracting).
Recording
If you want to capture and replay mouse movement you can try "recording" from the document.
This would use the x and y chords from the window.
To do this you can use the document DOM element:
var m = [];
// Using the document instead of body might solve your issue
$( document ).mousemove(function( e ){
m.push({ x : e.pageX, y : e.pageY });
});
Replaying
HTML/CSS
Your HTML/CSS should be a div on the page set with position: fixed which should match your javascript chord samples as fixed is absolutely positioned to the window:
<style>
.replay {
/* Use position fixed to match window chords */
position: fixed;
top: 0;
left: 0;
/* These are just for show */
border-radius: 20px;
background: red;
width: 10px;
height: 10px;
}
</style>
<div class="replay"></div>
Javascript
To replay your captured chords you can use something like this:
var $replay = $('.replay'), // Get mouse simulator
i = 0, l = m.length,
pos, t;
// Recursive animation function
function anim(){
// Cache current position
pos = m[i];
// Move to next position
$replay.css({ top: pos.y, left: pos.x });
i++;
// Exit recursive loop
if ( i === l )
clearTimeout( t );
// Or keep going
else
t = setTimeout(anim, 100); // Timeout speed controls animation speed
}
// Start animation loop
anim();
Demo
Try it out on this demo.

Categories