Merge pull request #78 in YR/star-web-components from feature-component-datepicker to master

* commit '6a16a7665a083b071e206bfb1912c0f39ac3f9a2':
  样式更改,修改响应事件
  TASK: #113920-Feature component timepicker
This commit is contained in:
汪昌棋 2022-10-13 18:36:58 +08:00
commit 95b29d6df4
4 changed files with 413 additions and 0 deletions

View File

@ -0,0 +1,156 @@
import {css, CSSResult} from 'lit'
export const timepickerStyles: CSSResult = css`
:host {
display: block;
}
* {
margin: 0;
padding: 0;
}
.btn {
height: 32px;
padding: 0 15px;
text-align: center;
font-size: 14px;
line-height: 32px;
color: #1890ff;
border: none;
background: #fff;
border-radius: 2px;
cursor: pointer;
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background: rgba(0, 0, 0, 0.6);
animation: fadeIn 0.3s forwards;
}
.slide-box {
position: fixed;
left: 0;
right: 0;
bottom: 30%;
padding: 15px;
border-radius: 20px;
background: #fff;
user-select: none;
text-align: center;
}
.fade-in {
animation: fadeIn 0.3s forwards;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fade-out {
animation: fadeOut 0.3s forwards;
}
@keyframes fadeOut {
from {
opacity: 10;
}
to {
opacity: 0;
}
}
.slide-up {
animation: slideUp 0.3s forwards;
}
@keyframes slideUp {
from {
transform: translate3d(0, 100%, 0);
}
to {
transform: translate3d(0, 0, 0);
}
}
.slide-down {
animation: slideDown 0.3s forwards;
}
@keyframes slideDown {
from {
transform: translate3d(0, 0, 0);
}
to {
transform: translate3d(0, 100%, 0);
}
}
h4 {
height: 24px;
margin-bottom: 16px;
font-size: 16px;
line-height: 24px;
text-align: center;
}
.picker-group {
display: flex;
}
.picker-column {
position: relative;
flex: 1;
height: 200px;
margin: 0 auto;
overflow: hidden;
touch-action: none;
}
.picker-column::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
height: 79px;
border-bottom: 1px solid #ebebeb;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.9),
rgba(255, 255, 255, 0.6)
);
}
.picker-column::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
height: 79px;
border-top: 1px solid #ebebeb;
background: linear-gradient(
to bottom,
rgba(255, 255, 255, 0.6),
rgba(255, 255, 255, 0.9)
);
}
li {
list-style: none;
font-size: 14px;
line-height: 40px;
text-align: center;
}
.picker_label {
margin: 15px auto 0;
}
.btn-cancel {
display: block;
margin: 15px auto 0;
float: left
}
.btn-sure {
display: block;
margin: 15px auto 0;
float: right;
}
`

View File

@ -0,0 +1,250 @@
import {LitElement, CSSResultArray, html} from 'lit'
import {customElement} from 'lit/decorators.js'
import {timepickerStyles} from './timepicker.css'
export class Picker {
public options: any
public isPointerdown: boolean
public itemHeight: number
public maxY: number
public minY: number
public lastY: number
public diffY: number
public translateY: number
public friction: number
public distanceY: number
public result: any
constructor(options: any) {
this.options = Object.assign({}, options)
this.isPointerdown = false
this.itemHeight = 40 // 列表项高度
this.maxY = this.itemHeight * 2
this.minY = this.itemHeight * (3 - this.options.list.length)
this.lastY = 0
this.diffY = 0
this.translateY = 0 // 当前位置
this.friction = 0.95 // 摩擦系数
this.distanceY = 0 // 滑动距离
this.result = this.options.list[0]
this.render()
this.bindEventListener()
}
render() {
let html = ''
for (const item of this.options.list) {
html += '<li>' + item + '</li>'
}
this.options.pickerContent.innerHTML = html
this.options.pickerContent.style.transform =
'translate3d(0px, ' + this.maxY + 'px, 0px)'
}
handlePointerdown(e: PointerEvent) {
// 如果是鼠标点击,只响应左键
if (e.pointerType === 'mouse' && e.button !== 0) {
return
}
this.options.pickerColumn.setPointerCapture(e.pointerId)
this.isPointerdown = true
this.lastY = e.clientY
this.diffY = 0
this.distanceY = 0
this.getTransform()
this.options.pickerContent.style.transform =
'translate3d(0px, ' + this.translateY + 'px, 0px)'
this.options.pickerContent.style.transition = 'none'
}
handlePointermove(e: PointerEvent) {
if (this.isPointerdown) {
this.diffY = e.clientY - this.lastY
this.translateY += this.diffY
this.lastY = e.clientY
this.options.pickerContent.style.transform =
'translate3d(0px, ' + this.translateY + 'px, 0px)'
}
}
handlePointerup() {
if (this.isPointerdown) {
this.isPointerdown = false
this.getTranslateY()
// 滑动距离与时长成正比且最短时长为300ms
const duration = Math.max(Math.abs(this.distanceY) * 1.5, 300)
this.options.pickerContent.style.transition =
'transform ' + duration + 'ms ease'
this.options.pickerContent.style.transform =
'translate3d(0px, ' + this.translateY + 'px, 0px)'
}
}
handlePointercancel() {
if (this.isPointerdown) {
this.isPointerdown = false
}
}
bindEventListener() {
this.handlePointerdown = this.handlePointerdown.bind(this)
this.handlePointermove = this.handlePointermove.bind(this)
this.handlePointerup = this.handlePointerup.bind(this)
this.handlePointercancel = this.handlePointercancel.bind(this)
this.options.pickerColumn.addEventListener(
'pointerdown',
this.handlePointerdown
)
this.options.pickerColumn.addEventListener(
'pointermove',
this.handlePointermove
)
this.options.pickerColumn.addEventListener(
'pointerup',
this.handlePointerup
)
this.options.pickerColumn.addEventListener(
'pointercancel',
this.handlePointercancel
)
}
getTransform() {
const transform = window
.getComputedStyle(this.options.pickerContent)
.getPropertyValue('transform')
this.translateY = parseFloat(transform.split(',')[5])
}
getTranslateY() {
let speed = this.diffY
while (Math.abs(speed) > 1) {
speed *= this.friction
this.distanceY += speed
}
// 边界判断
let y = this.translateY + this.distanceY
if (y > this.maxY) {
this.translateY = this.maxY
this.distanceY = this.maxY - this.translateY
} else if (y < this.minY) {
this.translateY = this.minY
this.distanceY = this.minY - this.translateY
} else {
this.translateY = y
}
// 计算停止位置使其为itemHeight的整数倍
let i = Math.round(this.translateY / this.itemHeight)
this.translateY = i * this.itemHeight
this.result = this.options.list[2 - this.translateY / this.itemHeight]
}
}
@customElement('star-timepicker')
export class TimePicker extends LitElement {
public static override get styles(): CSSResultArray {
return [timepickerStyles]
}
public openState:number = 0
// test
protected firstUpdated() {
const btnOpen = this.shadowRoot!.querySelector('.btn-open') as HTMLElement
const btnSure = this.shadowRoot!.querySelector('.btn-sure') as HTMLElement
const btnCancel = this.shadowRoot!.querySelector(
'.btn-cancel'
) as HTMLElement
const mask = this.shadowRoot!.querySelector('.mask') as HTMLElement
const slide = this.shadowRoot!.querySelector('.slide-box') as HTMLElement
if(this.openState==1){
}
btnOpen.addEventListener('click', function () {
mask.hidden = false
mask.classList.add('fade-in')
slide.classList.add('slide-up')
}, true)
// 点击非选择器区域关闭选择器
// mask.addEventListener('click', function (e) {
// let elm = e.target as HTMLElement
// if (elm.className.includes('mask')) {
// mask.classList.remove('fade-in')
// mask.classList.add('fade-out')
// slide.classList.remove('slide-up')
// slide.classList.add('slide-down')
// }
// })
btnCancel.addEventListener('click', function (e) {
let elm = e.target as HTMLElement
if (elm.className.includes('btn-cancel')) {
mask.classList.remove('fade-in')
mask.classList.add('fade-out')
slide.classList.remove('slide-up')
slide.classList.add('slide-down')
}
}, true)
mask.addEventListener('animationend', function (e) {
let elm = e.target as HTMLElement
if (elm.className.includes('fade-out')) {
mask.hidden = true
mask.classList.remove('fade-out')
slide.classList.remove('slide-down')
}
}, true)
btnSure.addEventListener('click', function (e) {
var a = new Date()
alert(hourPicker.result + ' : ' + minutePicker.result + a)
e.preventDefault()
}, true)
// 调用方式
function createList(start: number, end: number) {
const list = []
for (let i = start; i < end; i++) {
list[i] = i < 10 ? '0' + i : '' + i
}
return list
}
const hours = createList(0, 24),
minutes = createList(0, 60)
const pickerColumns = this.shadowRoot!.querySelectorAll('.picker-column')
const pickerContents = this.shadowRoot!.querySelectorAll('.picker-content')
const hourPicker = new Picker({
pickerColumn: pickerColumns[0],
pickerContent: pickerContents[0],
list: hours,
})
const minutePicker = new Picker({
pickerColumn: pickerColumns[1],
pickerContent: pickerContents[1],
list: minutes,
})
}
render() {
return html`
<button
class="btn btn-open"
type="button"
>
</button>
<div hidden class="mask">
<div class="slide-box">
<div id="checkbar">
<button class="btn btn-cancel" type="button"></button>
<span class="picker_label" style="display: inline-block">
</span>
<button class="btn btn-sure" type="button"></button>
</div>
<div class="picker-group">
<div class="picker-column">
<ul class="picker-content"></ul>
</div>
<div class="picker-column">
<ul class="picker-content"></ul>
</div>
</div>
</div>
</div>
`
}
}
declare global {
interface HTMLElementTagNameMap {
'star-timepicker': TimePicker
}
}

View File

@ -26,6 +26,7 @@ import './components/pattern-view/pattern-view'
import './components/overlay/active-overlay'
import './components/battery/battery'
import './components/battery-square/battery-square'
import './components/picker/timepicker'
@customElement('settings-app')
export class SettingsApp extends LitElement {
@query('star-animate-section#root') private rootSection!: StarAnimateSection

View File

@ -1,6 +1,7 @@
import {html, LitElement, CSSResultArray} from 'lit'
import {customElement} from 'lit/decorators.js'
import {sharedPickerStyles} from '../shared-picker-styles'
import '../../../components/picker/timepicker'
@customElement('panel-picker')
export class PanelPicker extends LitElement {
@ -69,6 +70,11 @@ export class PanelPicker extends LitElement {
<star-picker-option value="3">blue</star-picker-option>
</star-picker>
</label>
<label>
<span>datepicker</span>
<star-timepicker></star-timepicker>
</label>
</div>
`
}