UNPKG

vvcomponent

Version:
160 lines (153 loc) 4.66 kB
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 ? ` @keyframes 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);