mdui
Version:
a CSS Framework based on material design
218 lines (185 loc) • 5.44 kB
text/typescript
/**
* Inspired by https://github.com/nolimits4web/Framework7/blob/master/src/js/fast-clicks.js
* https://github.com/nolimits4web/Framework7/blob/master/LICENSE
*
* Inspired by https://github.com/fians/Waves
*/
import $ from 'mdui.jq/es/$';
import { JQ } from 'mdui.jq/es/JQ';
import 'mdui.jq/es/methods/addClass';
import 'mdui.jq/es/methods/attr';
import 'mdui.jq/es/methods/children';
import 'mdui.jq/es/methods/data';
import 'mdui.jq/es/methods/each';
import 'mdui.jq/es/methods/first';
import 'mdui.jq/es/methods/hasClass';
import 'mdui.jq/es/methods/innerHeight';
import 'mdui.jq/es/methods/innerWidth';
import 'mdui.jq/es/methods/off';
import 'mdui.jq/es/methods/offset';
import 'mdui.jq/es/methods/on';
import 'mdui.jq/es/methods/parents';
import 'mdui.jq/es/methods/prependTo';
import 'mdui.jq/es/methods/prop';
import 'mdui.jq/es/methods/remove';
import { isUndefined } from 'mdui.jq/es/utils';
import '../../jq_extends/methods/reflow';
import '../../jq_extends/methods/transform';
import '../../jq_extends/methods/transitionEnd';
import { $document } from '../../utils/dom';
import {
cancelEvent,
endEvent,
isAllow,
moveEvent,
register,
startEvent,
unlockEvent,
} from '../../utils/touchHandler';
/**
* 显示涟漪动画
* @param event
* @param $ripple
*/
function show(event: Event, $ripple: JQ): void {
// 鼠标右键不产生涟漪
if (event instanceof MouseEvent && event.button === 2) {
return;
}
// 点击位置坐标
const touchPosition =
typeof TouchEvent !== 'undefined' &&
event instanceof TouchEvent &&
event.touches.length
? event.touches[0]
: (event as MouseEvent);
const touchStartX = touchPosition.pageX;
const touchStartY = touchPosition.pageY;
// 涟漪位置
const offset = $ripple.offset();
const height = $ripple.innerHeight();
const width = $ripple.innerWidth();
const center = {
x: touchStartX - offset.left,
y: touchStartY - offset.top,
};
const diameter = Math.max(
Math.pow(Math.pow(height, 2) + Math.pow(width, 2), 0.5),
48,
);
// 涟漪扩散动画
const translate =
`translate3d(${-center.x + width / 2}px,` +
`${-center.y + height / 2}px, 0) scale(1)`;
// 涟漪的 DOM 结构,并缓存动画效果
$(
`<div class="mdui-ripple-wave" ` +
`style="width:${diameter}px;height:${diameter}px;` +
`margin-top:-${diameter / 2}px;margin-left:-${diameter / 2}px;` +
`left:${center.x}px;top:${center.y}px;"></div>`,
)
.data('_ripple_wave_translate', translate)
.prependTo($ripple)
.reflow()
.transform(translate);
}
/**
* 隐藏并移除涟漪
* @param $wave
*/
function removeRipple($wave: JQ): void {
if (!$wave.length || $wave.data('_ripple_wave_removed')) {
return;
}
$wave.data('_ripple_wave_removed', true);
let removeTimer = setTimeout(() => $wave.remove(), 400);
const translate = $wave.data('_ripple_wave_translate');
$wave
.addClass('mdui-ripple-wave-fill')
.transform(translate.replace('scale(1)', 'scale(1.01)'))
.transitionEnd(() => {
clearTimeout(removeTimer);
$wave
.addClass('mdui-ripple-wave-out')
.transform(translate.replace('scale(1)', 'scale(1.01)'));
removeTimer = setTimeout(() => $wave.remove(), 700);
setTimeout(() => {
$wave.transitionEnd(() => {
clearTimeout(removeTimer);
$wave.remove();
});
}, 0);
});
}
/**
* 隐藏涟漪动画
* @param this
*/
function hide(this: any): void {
const $ripple = $(this as HTMLElement);
$ripple.children('.mdui-ripple-wave').each((_, wave) => {
removeRipple($(wave));
});
$ripple.off(`${moveEvent} ${endEvent} ${cancelEvent}`, hide);
}
/**
* 显示涟漪,并绑定 touchend 等事件
* @param event
*/
function showRipple(event: Event): void {
if (!isAllow(event)) {
return;
}
register(event);
// Chrome 59 点击滚动条时,会在 document 上触发事件
if (event.target === document) {
return;
}
const $target = $(event.target as HTMLElement);
// 获取含 .mdui-ripple 类的元素
const $ripple = $target.hasClass('mdui-ripple')
? $target
: $target.parents('.mdui-ripple').first();
if (!$ripple.length) {
return;
}
// 禁用状态的元素上不产生涟漪效果
if ($ripple.prop('disabled') || !isUndefined($ripple.attr('disabled'))) {
return;
}
if (event.type === 'touchstart') {
let hidden = false;
// touchstart 触发指定时间后开始涟漪动画,避免手指滑动时也触发涟漪
let timer = setTimeout(() => {
timer = 0;
show(event, $ripple);
}, 200);
const hideRipple = (): void => {
// 如果手指没有移动,且涟漪动画还没有开始,则开始涟漪动画
if (timer) {
clearTimeout(timer);
timer = 0;
show(event, $ripple);
}
if (!hidden) {
hidden = true;
hide.call($ripple);
}
};
// 手指移动后,移除涟漪动画
const touchMove = (): void => {
if (timer) {
clearTimeout(timer);
timer = 0;
}
hideRipple();
};
$ripple.on('touchmove', touchMove).on('touchend touchcancel', hideRipple);
} else {
show(event, $ripple);
$ripple.on(`${moveEvent} ${endEvent} ${cancelEvent}`, hide);
}
}
$(() => {
$document.on(startEvent, showRipple).on(unlockEvent, register);
});