Skip to content

Instantly share code, notes, and snippets.

@elidupuis
Last active July 10, 2019 19:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save elidupuis/a5f6c133a409028daa0c4ad95291f8e2 to your computer and use it in GitHub Desktop.
Save elidupuis/a5f6c133a409028daa0c4ad95291f8e2 to your computer and use it in GitHub Desktop.
Parent animator offset x & y
import Ember from 'ember';
import move from 'ember-animated/motions/move';
import { fadeIn, fadeOut } from 'ember-animated/motions/opacity';
import adjustCSS from 'ember-animated/motions/adjust-css';
import { easeIn, easeOut, easeInAndOut } from 'ember-animated/easings/cosine';
export default Ember.Controller.extend({
appName: 'Ember Twiddle',
isThingActive: false,
showThingActions: true,
// change this to modify the animation speed
duration: 1500,
// a list of would be models or items
things: null,
init() {
this.set('things', this.makeItems(2));
},
actions: {
activate(thing) {
this.set('selectedThing', thing.id);
this.get('things').reverseObjects();
}
},
// manages vertical motion during list sorting
*transition({ keptSprites, duration }) {
for (let sprite of keptSprites) {
adjustCSS.property('margin-left');
adjustCSS.property('margin-right');
move(sprite, { duration, easing: easeOut });
}
},
// manages icons sliding in/out of list items
*iconTransition({ insertedSprites, keptSprites, removedSprites, duration }) {
// a slight slide in from the right
for (let sprite of insertedSprites) {
sprite.startTranslatedBy(50, 0);
move(sprite, { duration, easing: easeOut });
// fadeIn(sprite);
}
// handle interruption
for (let sprite of keptSprites) {
move(sprite, { duration, easing: easeOut });
// fadeIn(sprite);
}
// a slight slide out to the right
for (let sprite of removedSprites) {
sprite.endTranslatedBy(50, 0);
move(sprite, { duration, easing: easeIn });
// fadeOut(sprite);
}
},
// manages icons sliding in/out of list items and uses private API to keep vertical motion synced
*iconTransitionWithOffset({ insertedSprites, keptSprites, removedSprites, duration }) {
// a slight slide in from the right
for (let sprite of insertedSprites) {
let offsetDy = 0;
let offsetDx = 0;
// WARNING: this is private API.
// using the offsetParent's dy, we can keep the icons synced with the vertical motion of the container element (which is being moved by another animator).
let offsetSprite = sprite._offsetSprite;
if (offsetSprite) {
console.log('offsetSprite', offsetSprite);
offsetDy = offsetSprite.initialBounds.top - offsetSprite.finalBounds.top;
offsetDx = offsetSprite.initialBounds.left - offsetSprite.finalBounds.left;
}
sprite.startTranslatedBy(50 - offsetDx, -offsetDy);
move(sprite, { duration, easing: easeOut });
// fadeIn(sprite);
}
// handle interruption
for (let sprite of keptSprites) {
move(sprite, { duration, easing: easeOut });
// fadeIn(sprite);
}
// a slight slide out to the right
for (let sprite of removedSprites) {
sprite.endTranslatedBy(50, 0);
move(sprite, { duration, easing: easeIn });
// fadeOut(sprite);
}
},
makeItems(count = 5) {
let result = Ember.A();
for (let i = 0; i < count; i++) {
result.push(makeRandomItem(i));
}
return (result);
},
});
function makeRandomItem(index) {
var messages = ["Dwight", "Stanley", "Kelly", "Ryan", "Kevin"];
var images = ['https://pbs.twimg.com/profile_images/549268771484229632/WnatiHzT_400x400.jpeg', 'https://pbs.twimg.com/profile_images/1839546020/florida_stanley_400x400.jpg', 'https://pbs.twimg.com/profile_images/71405458/2928282474_24807334d7_400x400.jpg', 'https://pbs.twimg.com/profile_images/740436182107049984/y0N8Sqbi_400x400.jpg', 'https://pbs.twimg.com/profile_images/118888142/Brian_Baumgartner_134198_400x400.jpg'];
return { id: index, message: messages[index], image: images[index] };
}
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
}
.thing {
width: 50vw;
padding: 1rem;
margin: 0.5rem 0;
color: white;
background: steelblue;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
}
.hide-overflow .thing {
overflow: hidden;
}
.thing-1, .thing-3, .thing-5 {
background: orange;
}
.thing.selected {
margin-left: 2rem;
margin-right: 2rem;
}
.icons {
}
<h1>Nested Animator Offset</h1>
<div class="{{if hideOverflow 'hide-overflow'}}">
<fieldset>
<legend>Settings</legend>
<label>
<input type="checkbox" name="hide-overflow" onchange={{action (mut hideOverflow) (not hideOverflow)}} />
Overflow Hidden
</label>
</fieldset>
<h2>Simple Example</h2>
<p>Single animator that slides and fades icons. <br>This is the end goal for the icons in the nested example below.</p>
<div class="thing {{if isThingActive 'is-active'}}" {{action (mut isThingActive) (not isThingActive)}}>
<p>Some little thing (click me).</p>
{{#animated-if isThingActive use=iconTransition duration=duration}}
<div>✅</div>
{{else}}
<div>❎</div>
{{/animated-if}}
</div>
<hr/>
<h2>Nested Example</h2>
<p>Nested animator that slides and fades icons.</p>
<p><strong>⬇️ Click the first item to flip the list sorting</strong>. <br>Notice the inserted icons' starting position is offset by some amount and is not synced with the parent animator vertical motion.</p>
<p>The vertical movement is simply the list sort order changing.</p>
<p>Selected Thing ID: <code>{{selectedThing}}</code></p>
<AnimatedContainer>
{{#animated-each things key="id" use=transition duration=duration as |thing|}}
<div class="thing thing-{{thing.id}} {{if (eq thing.id selectedThing) 'selected'}}" {{action 'activate' thing}}>
<p>[{{thing.id}}] {{thing.message}}</p>
<AnimatedContainer>
{{#animated-if (eq thing.id selectedThing) use=iconTransition duration=duration}}
<div class="icons enabled">✅</div>
{{else}}
<div class="icons disabled">❎</div>
{{/animated-if}}
</AnimatedContainer>
</div>
{{/animated-each}}
</AnimatedContainer>
<hr/>
<h2>Nested Example with Adjusted Offset</h2>
<p>Here, the icon transition uses the private API of <code>sprite._offsetParent</code> to account for the changing vertical position of the parent animator.</p>
<p><strong>⬇️ Click the first item</strong> and notice that the inserted icons now properly follow the parent animator vertical motion.</p>
<p>Selected Thing ID: <code>{{selectedThing}}</code></p>
<AnimatedContainer>
{{#animated-each things key="id" use=transition duration=duration as |thing|}}
<div class="thing thing-{{thing.id}} {{if (eq thing.id selectedThing) 'selected'}}" {{action 'activate' thing}}>
<p>[{{thing.id}}] {{thing.message}}</p>
<AnimatedContainer>
{{#animated-if (eq thing.id selectedThing) use=iconTransitionWithOffset duration=duration}}
<div class="icons enabled">✅</div>
{{else}}
<div class="icons disabled">❎</div>
{{/animated-if}}
</AnimatedContainer>
</div>
{{/animated-each}}
</AnimatedContainer>
</div>
<br>
<br>
{{outlet}}
<br>
<br>
{
"version": "0.15.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js",
"ember": "3.4.3",
"ember-template-compiler": "3.4.3",
"ember-testing": "3.4.3"
},
"addons": {
"ember-animated": "0.6.1",
"ember-truth-helpers": "2.1.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment