Lottie player do not update the underlying animation when src attribute is changed at run time? - web-component

I was using lottie player for animated sticker on my PWA app.
Now at run time when I change the src attribute of lottie player, it do not reloads a new animation. <lottie-player src=${state.url} background="transparent" speed="1" style="width: 100%; height: 100%;" loop autoplay></lottie-player>
is there a way to force this animation to reload by changing the attribute ?
I am using lit-html as my templating library and custom framework for creating web components.

The lottie web player requires a programmatic load method call to change the animation. Their api doesn't update the animation based on a runtime src property change.
Lit-html and web components are not required to answer the question, but I can provide a code sample using lit to show a working example.
Example changing animation of Lottie web player: https://lit.dev/playground/#gist=4569b4b023db7014d45112679059296b
See below for a minimal answer.
class ExampleChangingAnimation extends LitElement {
/**
* Get the rendered Lottie player element
*/
get lottiePlayer() {
return this.shadowRoot.querySelector('lottie-player');
}
/**
* changeAnimation gets the new animation src string and loads
* it onto the Lottie web player.
*/
changeAnimation () {
// Get new animation to load.
const newAnimation = "...animation url...";
// Load the new animation on the lottie player element.
this.lottiePlayer.load(newAnimation)
}
render () {
return html`
<lottie-player></lottie-player>
<button #click=${this.changeAnimation}>Change Animation</button>
`;
}
}

Related

change global css element

i've made this banner like screen that appears when my site is loaded, but here's the thing, i don't want no scrollbar while this opening animation it's happening, i only want to show the other components (the scrollbar and the whole site) once the gsap animation finishes, how could i proceed? thanks! (i tried to create a function to control those global elements, is it a way?)
So if I understand correctly you need the Banner to be displayed until the site is loaded. Maybe you are making some API calls or in general, you are planning to show the banner for let's say 3 sec and post that you want your actual components to be displayed.
You can try below approch:
export const APP = (): JSX.Element => {
const [isAnimationInProgress, SetAnimationState] = React.useState(true);
React.useEffect(() => {
// You can have your page load API calls done here
// Or wait for 'X' seconds
// Post that set the AnimationState to false to render actual components
setAnimationState(false);
})
return (
{
isAnimationInProgress && <Banner />
}
{
!isAnimationInProgress && <ActualComponent />
}
)
}
Regarding scrollbars, including overflow: hidden; in style for the banner should do the work if you are getting scrollbars for the Banner component.

Avatar animation sync issue in NAF (Network a-frame)

I have avatar with animations, like flying, floating, eyes, etc. and perform specific animation based on keycode mapping with specific animation.
to perform specific animation on specific key, i have created custom animation component as follow schema:
schema: {
keyCode: { default: stopAllAnimation }
}
and put that element and component in network schema:
{
selector: ".AvatarRoot",
component: "own-animation"
}
Locally/Individually it is working fine but in NAF I am facing the syncing issue like play specific animation not sync in other tab/screen.
Any one know how avatar animation works in network a-frame? and cloud be possible to sync perfectly.
You need to make things with a-frame schema and register component with a-frame extras included to make animation from gtlf/glb models.
The flow is like you need to set the register component with clip name in local JS file like this
let currentKeyDown = null;
document.addEventListener("keydown", (e) => {
currentKeyDown = e.which;
const avatar = document.querySelector("#local-avatar");
switch (currentKeyDown) {
case 37: // left arrow key
avatar.setAttribute("player-info", "clip:Walk");
break;
}
});
and in register component if previous clip and new clip are different set a new clip. like this
if (oldData.clip !== this.data.clip) {
this.character.setAttribute('animation-mixer', {clip: this.data.clip, crossFadeDuration: 1});
}
Maybe you can take this code for reference and I have added to glitch. Glitch walking demo

How would I apply Material-UI managed styles to non-material-ui, non-react elements?

I have an application where I'm using Material UI and its theme provider (using JSS).
I'm now incorporating fullcalendar-react, which isn't really a fully fledged React library - it's just a thin React component wrapper around the original fullcalendar code.
That is to say, that I don't have access to things like render props to control how it styles its elements.
It does however, give you access to the DOM elements directly, via a callback that is called when it renders them (eg. the eventRender method).
Here's a basic demo sandbox.
Now what I'm wanting to do is make Full Calendar components (eg, the buttons) share the same look and feel as the rest of my application.
One way to do this, is that I could manually override all of the styles by looking at the class names it's using and implementing the style accordingly.
Or - I could implement a Bootstrap theme - as suggested in their documentation.
But the problem with either of these solutions, is that that:
It would be a lot of work
I would have synchronisation problems, if I made changes to my MUI theme and forgot to update the calendar theme they would look different.
What I would like to do is either:
Magically convert the MUI theme to a Bootstrap theme.
Or create a mapping between MUI class names and the calendar class names, something like:
.fc-button = .MuiButtonBase-root.MuiButton-root.MuiButton-contained
.fc-button-primary= .MuiButton-containedPrimary
I wouldn't mind having to massage the selectors etc to make it work (ie. For example - MUI Buttons have two internal spans, whereas Full Calendar have just one). It's mostly about when I change the theme - don't want to have to change it in two places.
Using something like Sass with its #extend syntax would is what I have in mind. I could create the full-calendar CSS with Sass easily enough - but how would Sass get access to the MuiTheme?
Perhaps I could take the opposite approach - tell MUI 'Hey these class names here should be styled like these MUI classes'.
Any concrete suggestions on how I would solve this?
Here is my suggestion (obviously, it's not straight forward). Take the styles from the MUI theme and generate style tag based on it using react-helmet. To do it event nicely, I created a "wrapper" component that do the map. I implemented only the primary rule but it can be extended to all the others.
This way, any change you will do in the theme will affect the mapped selectors too.
import React from "react";
import { Helmet } from "react-helmet";
export function MuiAdapter({ theme }) {
if (!theme.palette) {
return <></>;
}
return (
<Helmet>
<style type="text/css">{`
.fc-button-primary {
background: ${theme.palette.primary.main}
}
/* more styles go here */
`}</style>
</Helmet>
);
}
And the use of the adapter
<MuiAdapter theme={theme} />
Working demo: https://codesandbox.io/s/reverent-mccarthy-3o856
You could create a mapping between MUI class names and the calendar class names by going through ref's. It's possible that this is not what some would call "best practice"...but it's a solution :). Note that I updated your component from a functional component to a class component, but you could accomplish this with hooks in a functional component.
Add refs
Add a ref to the MUI element you want to set as a reference, in your case the Button.
<Button
color="primary"
variant="contained"
ref={x => {
this.primaryBtn = x;
}}
>
And a ref to a wrapping div around the component you want to map to. You can't add it directly to the component since that wouldn't give us access to children.
<div
ref={x => {
this.fullCal = x;
}}
>
<FullCalendar
...
/>
</div>
Map classes
From componentDidMount() add whatever logic you need to target the correct DOM node (for your case, I added logic for type and matchingClass). Then run that logic on all FullCalendar DOM nodes and replace the classList on any that match.
componentDidMount() {
this.updatePrimaryBtns();
}
updatePrimaryBtns = () => {
const children = Array.from(this.fullCal.children);
// Options
const type = "BUTTON";
const matchingClass = "fc-button-primary";
this.mapClassToElem(children, type, matchingClass);
};
mapClassToElem = (arr, type, matchingClass) => {
arr.forEach(elem => {
const { tagName, classList } = elem;
// Check for match
if (tagName === type && Array.from(classList).includes(matchingClass)) {
elem.classList = this.primaryBtn.classList.value;
}
// Run on any children
const next = elem.children;
if (next.length > 0) {
this.mapClassToElem(Array.from(next), type, matchingClass);
}
});
};
This is maybe a little heavy handed, but it meets your future proof requirement for when you updated update Material UI. It would also allow you to alter the classList as you pass it to an element, which has obvious benefits.
Caveats
If the 'mapped-to' component (FullCalendar) updated classes on the elements you target (like if it added .is-selected to a current button) or adds new buttons after mounting then you'd have to figure out a way to track the relevant changes and rerun the logic.
I should also mention that (obviously) altering classes might have unintended consequences like a breaking UI and you'll have to figure out how to fix them.
Here's the working sandbox: https://codesandbox.io/s/determined-frog-3loyf

How to prevent css animation to reoccur when angular SSR bootstraps the app

I built a landing site using Angular. To make it SEO friendly, I had to do prerender it.
The landing page starts with an intro animation (using CSS' animation).
When I first load the page/full reload, the animation starts, and in the middle the app is being bootstrapped so ot restarts the animation again.
I was wondering if there's a way to prevent the animations to reoccur. I know that there are few questions that might be similar to mine, but none of them helped me.
I have tried to solve this issue by:
Adding initialNavigation in AppRoutingModule:
#NgModule({
imports: [RouterModule.forRoot(routes, { initialNavigation: 'enabled'})],
exports: [RouterModule]
})
export class AppRoutingModule {
}
AddingTransferHttpCacheModule and BrowserTransferStateModule to AppModule, and ServerTransferStateModule to AppServerModule.
You could do it, however it might not be the answer you're looking for.
First let's see what happens when a SSR Angular app starts in the browser:
The browser receives the server-side rendered page. The HTML is rendered, including your animation, and it starts playing. Then at the bottom of the page, the angular script tags are parsed, and the JS for the app starts downloading.
While the app is downloading, the animation is playing.
When the app is downloaded, it bootstraps, and when it's bootstrapped, it renders the whole DOM again, including your animation. During this process, the old animated elements are removed from the DOM, and then new animated elements are added. The animation starts playing the second time.
You can go around this in 2 ways:
either don't start the animation until the app is loaded on the client (add the animation with a custom class when the app runs in a browser)
or move the animation outside of your app, embed it manually to the index.html - this way when the SSR re-render happens it will not be affected. (this way you can't have the element inside your <app-root></app-root> elements)
Hope this helps!
Have you tried to save any indication of initial loading, for example with cookies as suggested.
And then wrap the animated element with [#.disabled]="indication", it should look like this:
<div [#.disabled]="isInitialized">
<div #someAnimation>
...
</div>
</div>
It's actually fairly simple - all you need to do is:
get your initial (server side generated) page url
store it in the TransferState
when client page is reloaded, check if the url is the same
if so, fast forward animation immediately right to the end
ensure it's only done for the initial navigation (as the server- to client-rendered transition only occurs once)
I've created a simple directive for this purpose. Just put it on any element that has a css animation e.g.
<div class="slideInAnimation" ssrPreventAnimationReplay></div>
Here it is:
#Directive({
selector: '[ssrPreventAnimationReplay]'
})
export class ServerSidePreventAnimationReplayDirective {
static key = makeStateKey('ssrPreventAnimationReplay');
static subscription: Subscription;
constructor(
private el: ElementRef,
private transferState: TransferState,
private router: Router,
private renderer: Renderer2,
#Inject(PLATFORM_ID) private platformId) {
}
ngOnInit() {
if (isPlatformServer(this.platformId))
this.transferState.set(ServerSidePreventAnimationReplayDirective.key, { url: this.router.url });
else {
let value = this.transferState.get(ServerSidePreventAnimationReplayDirective.key, null);
if (value && value.url == this.router.url) {
this.preventAnimation();
this.cancelOnNavigation();
}
}
}
ngOnDestroy() {
if (ServerSidePreventAnimationReplayDirective.subscription)
ServerSidePreventAnimationReplayDirective.subscription.unsubscribe();
}
private preventAnimation() {
this.renderer.setStyle(this.el.nativeElement, 'animation-duration', '0ms');
this.renderer.setStyle(this.el.nativeElement, 'animation-delay', '0ms');
}
private cancelOnNavigation() {
ServerSidePreventAnimationReplayDirective.subscription = this.router.events.subscribe(async event => {
if (event instanceof NavigationStart)
this.transferState.remove(ServerSidePreventAnimationReplayDirective.key);
});
}
}
You can disable angular animations in a similar fashion.
Hope it's of help to someone.
Import NoopAnimationsModule to the app.server.module.ts
#NgModule({
imports: [
NoopAnimationsModule,

How to use CSS :hover on a toolbar button in Addon SDK

My addon was originally built in XUL and I am trying to redesign it using the addons SDK, and am having troubles getting icons to change/highlight when I hover the mouse over them.
I know how to apply a css stylesheet to an Addon SDK toolbar and its elements (and how to fetch the right #id to use). This allows me to change the background-color on a button, but I can't seem to make :hover work to change the button image.
It works if I assign a javascript listener for a mouseover event to the button, but if I have lots of buttons or menu items then this is way overkill compared to css.
One problem is that the button image is set on the sdk button element and it is an attribute of the button.
Now, I have tried using a transparent image for the button element's attribute and then using css to supply the image. Using XUL I would apply the image for the button or menu item with list-style-image.
So, my question is: How do I get :hover working in my css for an SDK toolbar button?
Here is the toolbarbutton-icon XUL binding:
<binding id="toolbarbutton-image"
extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
<content>
<xul:image class="toolbarbutton-icon" xbl:inherits="src=image"/>
</content>
</binding>
xbl:inherits="src=image" means that the image inherits its src attribute from the image attribute of the <toolbarbutton> thus list-style-image CSS is ignored.
The image property is set when you create the button with SDK APIs. While it is true that you cannot create an SDK button without an image, you can cheat the system either by removing the image attribute afterwards or by using a transparent image and then styling it with background-image just like in the normal web:
const { browserWindows: windows } = require("sdk/windows");
const { viewFor } = require("sdk/view/core");
const { attachTo } = require("sdk/content/mod");
const { Style } = require("sdk/stylesheet/style");
const { ActionButton } = require("sdk/ui/button/action");
var myButton = ActionButton({
id: "my-button",
label: "My Button",
icon: { "24": "./transparent24.png" },
});
let self = require("sdk/self");
let path = self.data.url(); // alternatively use chrome.manifest to register resource or chrome path
let widgetId = "action-button--toolkitrequire-my-button"; // get this from the Browser Toolbox
let css = `
#${widgetId} .toolbarbutton-icon {
background-image: url(${path}/icon24.png);
max-width: 24px;
}
#${widgetId}:hover .toolbarbutton-icon {
background-image: url(${path}/icon24-hover.png);
}`;
let style = Style({ source: css }); // or { uri: `${path}/style.css` }
for (let w of windows)
attachTo(style, viewFor(w));
Keep in mind that other styling may apply to the image so you better use Browser Toolbox to inspect the DOM. I am overriding max-width in this example.

Resources