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:
commit
95b29d6df4
|
@ -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;
|
||||
}
|
||||
`
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue