mdui
Version:
实现 material you 设计规范的 Web Components 组件库
176 lines (175 loc) • 6.19 kB
JavaScript
import { __decorate } from "tslib";
import { html } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { createRef, ref } from 'lit/directives/ref.js';
import { $ } from '@mdui/jq/$.js';
import '@mdui/jq/methods/addClass.js';
import '@mdui/jq/methods/children.js';
import '@mdui/jq/methods/css.js';
import '@mdui/jq/methods/data.js';
import '@mdui/jq/methods/each.js';
import '@mdui/jq/methods/filter.js';
import '@mdui/jq/methods/innerHeight.js';
import '@mdui/jq/methods/innerWidth.js';
import '@mdui/jq/methods/offset.js';
import '@mdui/jq/methods/on.js';
import '@mdui/jq/methods/prependTo.js';
import '@mdui/jq/methods/remove.js';
import { MduiElement } from '@mdui/shared/base/mdui-element.js';
import { booleanConverter } from '@mdui/shared/helpers/decorator.js';
import { componentStyle } from '@mdui/shared/lit-styles/component-style.js';
import { style } from './style.js';
/**
* 处理点击时的涟漪动画;及添加 hover、focused、dragged 的背景色
* 背景色通过在 .surface 元素上添加对应的 class 实现
* 阴影在 ripple-mixin 中处理,通过在 :host 元素上添加 attribute 供 CSS 选择器添加样式
*/
let Ripple = class Ripple extends MduiElement {
constructor() {
super(...arguments);
/**
* 是否禁用涟漪动画
*/
this.noRipple = false;
this.hover = false;
this.focused = false;
this.dragged = false;
this.surfaceRef = createRef();
}
startPress(event) {
if (this.noRipple) {
return;
}
const $surface = $(this.surfaceRef.value);
const surfaceHeight = $surface.innerHeight();
const surfaceWidth = $surface.innerWidth();
// 点击位置坐标
let touchStartX;
let touchStartY;
if (!event) {
// 未传入事件对象,涟漪从中间扩散
touchStartX = surfaceWidth / 2;
touchStartY = surfaceHeight / 2;
}
else {
// 传入了事件对象,涟漪从点击位置扩散
const touchPosition = typeof TouchEvent !== 'undefined' &&
event instanceof TouchEvent &&
event.touches.length
? event.touches[0]
: event;
const offset = $surface.offset();
// 点击位置不在 surface 内,不执行
if (touchPosition.pageX < offset.left ||
touchPosition.pageX > offset.left + surfaceWidth ||
touchPosition.pageY < offset.top ||
touchPosition.pageY > offset.top + surfaceHeight) {
return;
}
touchStartX = touchPosition.pageX - offset.left;
touchStartY = touchPosition.pageY - offset.top;
}
// 涟漪直径
const diameter = Math.max(Math.pow(Math.pow(surfaceHeight, 2) + Math.pow(surfaceWidth, 2), 0.5), 48);
// 涟漪扩散动画
const translateX = `${-touchStartX + surfaceWidth / 2}px`;
const translateY = `${-touchStartY + surfaceHeight / 2}px`;
const translate = `translate3d(${translateX}, ${translateY}, 0) scale(1)`;
// 涟漪 DOM 元素
$('<div class="wave"></div>')
.css({
width: diameter,
height: diameter,
marginTop: -diameter / 2,
marginLeft: -diameter / 2,
left: touchStartX,
top: touchStartY,
})
.each((_, wave) => {
wave.style.setProperty('--mdui-comp-ripple-transition-x', translateX);
wave.style.setProperty('--mdui-comp-ripple-transition-y', translateY);
})
.prependTo(this.surfaceRef.value)
.each((_, wave) => wave.clientLeft) // 重绘
.css('transform', translate)
.on('animationend', function (e) {
const event = e;
if (event.animationName === 'mdui-comp-ripple-radius-in') {
$(this).data('filled', true); // 扩散动画完成后,添加标记
}
});
}
endPress() {
const $waves = $(this.surfaceRef.value)
.children()
.filter((_, wave) => !$(wave).data('removing'))
.data('removing', true);
const hideAndRemove = ($waves) => {
$waves
.addClass('out')
.each((_, wave) => wave.clientLeft) // 重绘
.on('animationend', function () {
$(this).remove();
});
};
// 扩散动画未完成,先完成扩散,再隐藏并移除
$waves
.filter((_, wave) => !$(wave).data('filled'))
.on('animationend', function (e) {
const event = e;
if (event.animationName === 'mdui-comp-ripple-radius-in') {
hideAndRemove($(this));
}
});
// 扩散动画已完成,直接隐藏并移除
hideAndRemove($waves.filter((_, wave) => !!$(wave).data('filled')));
}
startHover() {
this.hover = true;
}
endHover() {
this.hover = false;
}
startFocus() {
this.focused = true;
}
endFocus() {
this.focused = false;
}
startDrag() {
this.dragged = true;
}
endDrag() {
this.dragged = false;
}
render() {
return html `<div ${ref(this.surfaceRef)} class="surface ${classMap({
hover: this.hover,
focused: this.focused,
dragged: this.dragged,
})}"></div>`;
}
};
Ripple.styles = [componentStyle, style];
__decorate([
property({
type: Boolean,
reflect: true,
converter: booleanConverter,
attribute: 'no-ripple',
})
], Ripple.prototype, "noRipple", void 0);
__decorate([
state()
], Ripple.prototype, "hover", void 0);
__decorate([
state()
], Ripple.prototype, "focused", void 0);
__decorate([
state()
], Ripple.prototype, "dragged", void 0);
Ripple = __decorate([
customElement('mdui-ripple')
], Ripple);
export { Ripple };