// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview This element contains a set of SVGs that together acts as an
* animated and responsive background for any page that contains it.
*/
import 'chrome://resources/cr_elements/cr_icon_button/cr_icon_button.js';
import '../strings.m.js';
import {assert} from 'chrome://resources/js/assert.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
import {getCss} from './onboarding_background.css.js';
import {getHtml} from './onboarding_background.html.js';
export interface OnboardingBackgroundElement {
$: {
logo: HTMLElement,
};
}
export class OnboardingBackgroundElement extends CrLitElement {
static get is() {
return 'onboarding-background';
}
static override get styles() {
return getCss();
}
override render() {
return getHtml.bind(this)();
}
static override get properties() {
return {
forcePaused_: {
type: Boolean,
reflect: true,
},
};
}
private animations_: Animation[] = [];
private forcePaused_: boolean =
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
override connectedCallback() {
super.connectedCallback();
const details: Array<[string, number]> = [
['blue-line', 60],
['green-line', 68],
['red-line', 45],
['grey-line', 68],
['yellow-line', 49],
];
details.forEach(([id, width]) => {
const element = this.shadowRoot!.querySelector<HTMLElement>(`#${id}`);
assert(element);
this.createLineAnimation_(element, width);
});
}
private createLineAnimation_(lineContainer: HTMLElement, width: number) {
const line = lineContainer.firstElementChild as HTMLElement;
const lineFill = line.firstElementChild as HTMLElement;
const pointOptions = {
endDelay: 3250,
fill: 'forwards' as FillMode,
duration: 750,
};
const startPointAnimation = lineFill.animate(
[
{width: '0px'},
{width: `${width}px`},
],
Object.assign({}, pointOptions, {easing: 'cubic-bezier(.6,0,0,1)'}));
startPointAnimation.pause();
this.animations_.push(startPointAnimation);
this.loopAnimation_(startPointAnimation);
const endPointWidthAnimation = line.animate(
[
{width: `${width}px`},
{width: '0px'},
],
Object.assign(
{}, pointOptions, {easing: 'cubic-bezier(.66,0,.86,.25)'}));
endPointWidthAnimation.pause();
this.animations_.push(endPointWidthAnimation);
this.loopAnimation_(endPointWidthAnimation);
const endPointTransformAnimation = line.animate(
[
{transform: `translateX(0)`},
{transform: `translateX(${width}px)`},
],
Object.assign({}, pointOptions, {
easing: 'cubic-bezier(.66,0,.86,.25)',
}));
endPointTransformAnimation.pause();
this.animations_.push(endPointTransformAnimation);
this.loopAnimation_(endPointTransformAnimation);
const lineTransformAnimation = lineContainer.animate(
[
{transform: `translateX(0)`},
{transform: `translateX(40px)`},
],
{
composite: 'add', // There is already a rotate on the line.
duration: 1500,
easing: 'cubic-bezier(0,.56,.46,1)',
endDelay: 2500,
fill: 'forwards',
});
lineTransformAnimation.pause();
this.animations_.push(lineTransformAnimation);
this.loopAnimation_(lineTransformAnimation);
}
protected getPlayPauseIcon_(): string {
return this.forcePaused_ ? 'welcome:play' : 'welcome:pause';
}
protected getPlayPauseLabel_(): string {
return loadTimeData.getString(
this.forcePaused_ ? 'landingPlayAnimations' : 'landingPauseAnimations');
}
private loopAnimation_(animation: Animation) {
// Animations that have a delay after them can only be looped by re-playing
// them as soon as they finish. The |endDelay| property of JS animations
// only works if |iterations| is 1, and the |delay| property runs before
// the animation even plays.
animation.onfinish = () => {
animation.play();
};
}
protected onLogoClick_() {
this.$.logo.animate(
{
transform: [
'translate(-50%, -50%)',
'translate(-50%, -50%) rotate(-10turn)',
],
},
{
duration: 500,
easing: 'cubic-bezier(1, 0, 0, 1)',
});
}
protected onPlayPauseClick_() {
if (this.forcePaused_) {
this.play();
} else {
this.pause();
}
this.forcePaused_ = !this.forcePaused_;
}
pause() {
this.animations_.forEach(animation => animation.pause());
}
play() {
if (this.forcePaused_) {
return;
}
this.animations_.forEach(animation => animation.play());
}
}
customElements.define(
OnboardingBackgroundElement.is, OnboardingBackgroundElement);