vvcomponent
Version:
VV组件
160 lines (153 loc) • 4.66 kB
JavaScript
class DropdownRefresh extends HTMLElement {
#element = null;
#isTouch = false;
#touchStart = {};
#touchMove = {};
#touchEnd = {};
#loading = false;
static get observedAttributes() {
return ['element'];
}
constructor() {
super();
if (!/mobile/i.test(navigator.userAgent)) {
throw 'This component is only available on mobile devices.';
}
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `<style>
${this.#genStyle(false)}
</style>
<div class="loader-main">
<div class="loader"></div>
</div>
<div class="content">
${this.innerHTML}
</div>`
const observer = new MutationObserver(e => {
this.shadowRoot.querySelector(".content").innerHTML = this.innerHTML;
this.connectedCallback();
});
observer.observe(this, { childList: true, subtree: true, attributes: true, characterData: true, attributeOldValue: true, characterDataOldValue: true });
this.addEventListener('touchstart', this._TouchStart);
this.addEventListener('touchmove', this._TouchMove);
this.addEventListener('touchend', this._TouchEnd);
}
connectedCallback() {
if (!this.#element) return;
this.#getStyle().innerHTML = this.#genStyle(false);
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'element') {
this.#element = this.querySelector(newValue);
this.connectedCallback();
}
}
#genStyle(isLoading, dy = 0, animationDelay = 0) {
return ` :host {
overflow: hidden;
}
.loader-main {
width: 40px;
height: 40px;
background-color: white;
border-radius: 50%;
padding: 5px;
position: absolute;
top: ${-40 + dy}px;
left: calc(50% - 25px);
}
.loader {
width: 30px;
height: 30px;
display: inline-block;
border: 5px solid rgb(20, 150, 255);
border-radius: 50%;
border-top-color: transparent;
border-bottom-color: transparent;
animation: rot5 1s infinite;
}${isLoading ? `
rot5 {
0% {
transform: rotate(0);
}
50% {
transform: rotate(180deg);
border-top-color: rgb(90, 150, 255);
border-bottom-color: rgb(100, 200, 255);
border-right-color: transparent;
border-left-color: transparent;
}
100% {
transform: rotate(360deg);
}
}` : ""}`;
}
#getStyle() {
return this.shadowRoot.querySelector("style");
}
_TouchStart(e) {
if (this.scrollTop != 0) return;
console.log("开始", e);
this.#isTouch = true;
this.dispatchEvent(new CustomEvent("start", { bubbles: true, composed: true }));
const touch = e.touches[0];
console.log(touch);
this.#touchStart = { y: touch.clientY };
}
_TouchEnd(e) {
if (this.scrollTop != 0) return;
console.log("结束", e);
this.#isTouch = false;
const touch = e.changedTouches[0];
this.#touchEnd = { y: touch.clientY };
if (touch.clientY - this.#touchStart.y < 100) {
this.#recover();
return;
};
this.#loading = true;
this.dispatchEvent(new CustomEvent("end", { bubbles: true, composed: true }));
this.#getStyle().innerText = this.#genStyle(true, 100);
this.style.pointerEvents = "none";
}
_TouchMove(e) {
if (this.scrollTop != 0) return;
if (!this.#isTouch) return;
console.log("移动", e);
const touch = e.touches[0];
this.#touchMove = { y: touch.clientY };
const dy = this.#touchMove.y - this.#touchStart.y;
console.log(dy);
if (dy <= 0) {
this.#getStyle().innerText = this.#genStyle(false, 0);
return;
};
if (dy > 100) return;
this.#getStyle().innerText = this.#genStyle(false, dy);
}
endload() {
if (!this.#loading) return;
this.#recover();
}
#recover() {
// if (!this.#loading || !this.#isTouch) return;
const that = this;
const dy = this.#touchMove.y - this.#touchStart.y > 100 ? 100 : this.#touchMove.y - this.#touchStart.y;
console.log(dy)
let times = 1;
function animation() {
console.log(dy - times * 10)
if (dy - times * 10 < 0) {
that.#getStyle().innerText = that.#genStyle(false, 0);
that.style.pointerEvents = null;
that.#loading = false;
return;
}
that.#getStyle().innerText = that.#genStyle(false, dy - times * 10);
times++;
console.log(times)
requestAnimationFrame(animation)
}
requestAnimationFrame(animation)
}
}
customElements.define('iftc-dropdownrefresh', DropdownRefresh);