Merge pull request #19 in YR/star-web-components from feature-component-slider to master
* commit '7bf0619903d54f572a82b2989902dc87248bb8e5': TASK: #109540 行slider中tick属性结构样式改变、跳格运动以及跟手跳格。 TASK: #109540 列slider跟手拖动,行slider部分优化 TASK: #slider中tick属性实现及优化 TASK: #点击sliderBar小球跳转以及disabled属性、unfilled属性
This commit is contained in:
commit
181443e04b
|
@ -3,6 +3,7 @@
|
|||
工作职责:
|
||||
|
||||
- 滑块空间
|
||||
- 滑块拖动后返回value值
|
||||
|
||||
## 类型包括:
|
||||
|
||||
|
@ -11,28 +12,56 @@
|
|||
```
|
||||
<star-slider></star-slider>
|
||||
```
|
||||
|
||||
2. 滑块中小球左侧进行填充 --- `filled`
|
||||
2. `coverWidth` --- 初始填充
|
||||
|
||||
```
|
||||
<star-slider variant="filled"></star-slider>
|
||||
<star-slider coverWidth="150px"></star-slider>
|
||||
<star-slider coverWidth="50%"></star-slider>
|
||||
```
|
||||
|
||||
3. 禁用滑块 --- `disabled`
|
||||
3. `disabled` --- 禁用滑块
|
||||
|
||||
```
|
||||
<star-slider disabled></star-slider>
|
||||
<star-slider disabled coverWidth="150px"></star-slider>
|
||||
```
|
||||
|
||||
4. 分格滑块 --- `Tick`
|
||||
4. `unfilled` --- 滑块中小球左侧不进行填充
|
||||
|
||||
```
|
||||
<star-slider variant="tick" tick-step="20"></star-slider>
|
||||
<star-slider variant="tick" tick-step="20" disabled></star-slider>
|
||||
<star-slider unfilled></star-slider>
|
||||
<star-slider unfilled coverWidth="30%"></star-slider>
|
||||
<star-slider unfilled coverWidth="30%" disabled></star-slider>
|
||||
```
|
||||
|
||||
5. 左侧图标|滑块|右侧图标
|
||||
|
||||
5. `Tick` --- 分格滑块(默认是unfilled属性)
|
||||
min=0,mix=100,按照需求填写step(每一格)的大小<br>
|
||||
example : <br>
|
||||
step="25" 表示把slider分为4块<br>
|
||||
coverWidth="40%" 表示初始小球落在第二格上
|
||||
|
||||
|
||||
|
||||
|
||||
```
|
||||
<star-slider ><slot></slot></star-slider>
|
||||
<star-slider tick step="10"></star-slider>
|
||||
<star-slider tick step="20" coverWidth="40%" disabled></star-slider>
|
||||
```
|
||||
6. `vertical` --- 垂直slider
|
||||
|
||||
```
|
||||
<star-slider vertical></star-slider>
|
||||
<star-slider vertical disabled></star-slider>
|
||||
<star-slider vertical coverWidth="50%"></star-slider>
|
||||
```
|
||||
7. 左侧图标|滑块|右侧图标
|
||||
|
||||
```
|
||||
<star-slider>
|
||||
<p class="left" data-icon="brightness"></p>
|
||||
</star-slider>
|
||||
```
|
||||
|
||||
## 后续需解决的问题:
|
||||
- tick 属性中小球不能完全覆盖step
|
||||
- vertical 属性变化太快
|
||||
|
|
|
@ -1,16 +1,38 @@
|
|||
import {css, CSSResult} from 'lit'
|
||||
export const sharedStyles: CSSResult = css`
|
||||
:host {
|
||||
--cover-width: 100px;
|
||||
--dot-move: 87px;
|
||||
--cover-width: 0px;
|
||||
--dot-move: 0px;
|
||||
--vWidth: 8px;
|
||||
}
|
||||
.content {
|
||||
margin: 5px 5px;
|
||||
height: 2px;
|
||||
margin: 50px;
|
||||
position: relative;
|
||||
padding: 50px 50px;
|
||||
border: 1px solid skyblue;
|
||||
padding: 8px;
|
||||
/*border: 1px solid skyblue;*/
|
||||
border-radius: 5px;
|
||||
}
|
||||
.v-content {
|
||||
width: 2px;
|
||||
margin: 0px 2px;
|
||||
position: relative;
|
||||
padding: 200px 10px;
|
||||
/*border: 1px solid skyblue;*/
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.v-sliderBar {
|
||||
position: absolute;
|
||||
width: var(--vWidth);
|
||||
height: 80%;
|
||||
left: calc(50% - var(--vWidth) / 2);
|
||||
top: 0px;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sliderBar {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
@ -18,32 +40,93 @@ export const sharedStyles: CSSResult = css`
|
|||
left: 0px;
|
||||
right: 0px;
|
||||
top: calc(50% - 6px / 2);
|
||||
background: rgba(0, 0, 0, 0.06);
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.v-progress {
|
||||
position: absolute;
|
||||
width: var(--vWidth);
|
||||
height: var(--cover-width);
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
border-radius: 0 0 20px 20px;
|
||||
background: #404040;
|
||||
}
|
||||
.progress {
|
||||
position: absolute;
|
||||
width: var(--cover-width);
|
||||
/*width: 100px;*/
|
||||
height: 6px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
top: calc(50% - 6px / 2);
|
||||
background: #4d4d4d;
|
||||
border-radius: 5px;
|
||||
border-radius: 5px 0 0 5px;
|
||||
}
|
||||
.dot {
|
||||
position: absolute;
|
||||
left: var(--dot-move);
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
top: calc(50% - 26px / 2);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: calc(50% - 20px / 2);
|
||||
background: #544f4f;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
}
|
||||
p {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 1px;
|
||||
}
|
||||
:host([disabled]) .progress {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
:host([disabled]) .v-progress {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
:host([disabled]) .step {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
:host([tick]) .step {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
:host([tick]) .sliderBar {
|
||||
background: #c0c0c0;
|
||||
border-radius: 0 0 0 0;
|
||||
height: 4px;
|
||||
}
|
||||
:host([disabled]) .dot {
|
||||
background: #c0c0c0;
|
||||
}
|
||||
:host([unfilled]) .progress {
|
||||
background: none;
|
||||
}
|
||||
:host([tick]) .progress {
|
||||
background: none;
|
||||
}
|
||||
.step {
|
||||
position: absolute;
|
||||
left: calc(var(--i) * 99.3%);
|
||||
top: calc(50% - 10px / 2);
|
||||
width: 3px;
|
||||
height: 10px;
|
||||
background-color: #d5d5d5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
::slotted(span)::before {
|
||||
font-size: 27px;
|
||||
font-family: 'gaia-icons';
|
||||
content: attr(data-icon);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: 357px;
|
||||
}
|
||||
::slotted(p)::before {
|
||||
font-size: 27px;
|
||||
font-family: 'gaia-icons';
|
||||
content: attr(data-icon);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
top: 74px;
|
||||
left: 5px;
|
||||
}
|
||||
`
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import {html, LitElement, CSSResultArray} from 'lit'
|
||||
import {html, LitElement, CSSResultArray, PropertyValueMap} from 'lit'
|
||||
import {customElement, property, query} from 'lit/decorators.js'
|
||||
import {sharedStyles} from './slider-styles'
|
||||
|
||||
export const variants = ['filled', 'tick']
|
||||
|
||||
@customElement('star-slider')
|
||||
export class StarSlider extends LitElement {
|
||||
_coverWidth: string = ''
|
||||
|
@ -14,21 +12,28 @@ export class StarSlider extends LitElement {
|
|||
@query('.content') content!: HTMLDivElement
|
||||
@query('.sliderBar') sliderBar!: HTMLDivElement
|
||||
@query('.progress') progress!: HTMLDivElement
|
||||
@query('.v-sliderBar') vSliderBar!: HTMLDivElement
|
||||
@query('.v-progress') vProgress!: HTMLDivElement
|
||||
@query('.dot') dot!: HTMLDivElement
|
||||
@query('p') p!: HTMLParagraphElement
|
||||
|
||||
@property({type: Boolean}) disabled = false
|
||||
@property({type: Boolean}) tick = false
|
||||
@property({type: Boolean}) vertical = false
|
||||
@property({type: Number}) startX = 0
|
||||
@property({type: Number}) startY = 0
|
||||
@property({type: Number}) touchX = 0
|
||||
@property({type: Number}) touchY = 0
|
||||
@property({type: Number}) moveX = 0
|
||||
@property({type: Number}) moveY = 0
|
||||
@property({type: Number}) newX = 0
|
||||
@property({type: Number}) newY = 0
|
||||
@property({type: Number}) barWidth = 0
|
||||
@property({type: Number}) dotL = 0
|
||||
@property({type: Number}) proportion = 0
|
||||
@property({type: String}) pValue = ''
|
||||
@property({type: Number}) sliderBarLeft = 0
|
||||
@property({type: Number}) sliderBarRight = 0
|
||||
@property({type: Number}) ball = 0
|
||||
@property({type: String}) sliderCoverWidth = ''
|
||||
@property({type: String}) ballMove = ''
|
||||
@property({type: Number}) barX = 0
|
||||
@property({type: String}) endValue = ''
|
||||
@property({type: String}) step = ''
|
||||
@property({type: String})
|
||||
get coverWidth() {
|
||||
return this._coverWidth
|
||||
|
@ -36,57 +41,202 @@ export class StarSlider extends LitElement {
|
|||
set coverWidth(value: string) {
|
||||
this.style.setProperty('--cover-width', value)
|
||||
this._coverWidth = value
|
||||
this.style.setProperty('--dot-move', this._coverWidth)
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p>${this.pValue}</p>
|
||||
<div class="sliderBar">
|
||||
<div class="progress" coverWidth="100px"></div>
|
||||
<div
|
||||
class="dot"
|
||||
@touchstart=${this.touchStart}
|
||||
@touchend=${this.touchEnd}
|
||||
@touchmove=${this.touchMove}
|
||||
></div>
|
||||
if (!this.vertical) {
|
||||
return html`
|
||||
<slot></slot>
|
||||
<div
|
||||
class="content"
|
||||
id="content"
|
||||
@touchstart=${this.touchStart}
|
||||
@touchend=${this.touchEnd}
|
||||
@touchmove=${this.touchMove}
|
||||
>
|
||||
<div class="sliderBar">
|
||||
<div class="progress"></div>
|
||||
<div class="dot"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
`
|
||||
} else {
|
||||
return html`
|
||||
<slot></slot>
|
||||
<div
|
||||
class="v-content"
|
||||
@touchstart=${this.touchStartVertical}
|
||||
@touchend=${this.touchEndVertical}
|
||||
@touchmove=${this.touchMoveVertical}
|
||||
>
|
||||
<div class="v-sliderBar">
|
||||
<div class="v-progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
protected firstUpdated(
|
||||
_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>
|
||||
): void {
|
||||
if (this.vertical) {
|
||||
this.touchStart = this.touchStartVertical
|
||||
this.touchEnd = this.touchEndVertical
|
||||
this.touchMove = this.touchMoveVertical
|
||||
}
|
||||
if (this.tick) {
|
||||
var tickStep = 100 / parseInt(this.step)
|
||||
for (let i = 0; i <= tickStep; i++) {
|
||||
const stepTick = document.createElement('div')
|
||||
stepTick.style.setProperty('--i', String(i / tickStep))
|
||||
stepTick.classList.add('step')
|
||||
this.sliderBar.appendChild(stepTick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private touchStart(evt: TouchEvent) {
|
||||
this.barWidth = this.sliderBar.offsetWidth - this.dot.offsetWidth //总长度减去小球覆盖的部分
|
||||
this.dotL = this.dot.offsetLeft //小球左侧相对于父元素的左边距
|
||||
this.startX = evt.touches[0].clientX //手指点下的 X 坐标
|
||||
if (!this.disabled) {
|
||||
this.touchX = evt.touches[0].clientX //手指触摸的 x 坐标
|
||||
// 黑色覆盖部分
|
||||
this.barX =
|
||||
this.touchX -
|
||||
this.sliderBar.getBoundingClientRect().left -
|
||||
this.dot.offsetWidth * 0.5
|
||||
this.barWidth = this.sliderBar.offsetWidth - this.dot.offsetWidth
|
||||
if (this.barX < 0) {
|
||||
this.barX = 0
|
||||
}
|
||||
if (this.barX >= this.barWidth) {
|
||||
this.barX = this.barWidth
|
||||
}
|
||||
this.proportion = (this.barX / this.barWidth) * 100
|
||||
this.endValue = Math.ceil(this.proportion) + ''
|
||||
if (this.tick) {
|
||||
// tick跳格滑动
|
||||
var tickStep = 100 / parseInt(this.step) //分为几格
|
||||
for (let i = 0; i <= tickStep; i++) {
|
||||
if (this.barX > (this.barWidth * (i + 0.5)) / tickStep) {
|
||||
this.style.setProperty(
|
||||
'--dot-move',
|
||||
(this.barWidth * (i + 1)) / tickStep + 'px'
|
||||
)
|
||||
}
|
||||
if (this.barX == 0) {
|
||||
this.style.setProperty('--dot-move', 0 + 'px')
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.style.setProperty('--dot-move', this.barX + 'px')
|
||||
this.progress.style.setProperty(
|
||||
'width',
|
||||
(this.barWidth * Math.ceil(this.proportion)) / 100 + 'px'
|
||||
)
|
||||
}
|
||||
// 重新初始化防止滑块增量
|
||||
this.startX = evt.touches[0].clientX //手指点下的初始 X&Y 坐标
|
||||
this.barWidth = this.sliderBar.offsetWidth - this.dot.offsetWidth //总长度减去小球覆盖的部分
|
||||
this.dotL = this.dot.offsetLeft //小球左侧相对于父元素的左边距
|
||||
}
|
||||
}
|
||||
private touchMove(evt: TouchEvent) {
|
||||
//阻止默认行为
|
||||
evt.preventDefault()
|
||||
this.touchX = evt.touches[0].clientX //整个屏幕实时触摸的 X 坐标
|
||||
this.moveX = this.touchX - this.startX //手指移动的距离
|
||||
//判断最大值和最小值
|
||||
this.newX = this.dotL + this.moveX
|
||||
if (this.newX < 0) {
|
||||
this.newX = 0
|
||||
if (!this.disabled) {
|
||||
//阻止默认行为
|
||||
evt.preventDefault()
|
||||
this.touchX = evt.touches[0].clientX //整个屏幕实时触摸的 X 坐标
|
||||
this.moveX = this.touchX - this.startX //手指移动的距离
|
||||
|
||||
//判断最大值和最小值
|
||||
this.newX = this.dotL + this.moveX
|
||||
|
||||
if (this.newX < 0) {
|
||||
this.newX = 0
|
||||
}
|
||||
if (this.newX >= this.barWidth) {
|
||||
this.newX = this.barWidth
|
||||
}
|
||||
//计算比例
|
||||
this.proportion = (this.newX / this.barWidth) * 100
|
||||
//取整
|
||||
this.endValue = Math.ceil(this.proportion) + ''
|
||||
if (!this.tick) {
|
||||
this.style.setProperty('--dot-move', this.newX + 'px')
|
||||
this.progress.style.setProperty(
|
||||
'width',
|
||||
(this.barWidth * Math.ceil(this.proportion)) / 100 + 'px'
|
||||
)
|
||||
} else {
|
||||
// tick跳格滑动
|
||||
var tickStep = 100 / parseInt(this.step) //分为几格
|
||||
for (let i = 0; i <= tickStep; i++) {
|
||||
if (this.newX > (this.barWidth * (i + 0.5)) / tickStep) {
|
||||
this.style.setProperty(
|
||||
'--dot-move',
|
||||
(this.barWidth * (i + 1)) / tickStep + 'px'
|
||||
)
|
||||
this.proportion =
|
||||
((this.barWidth * (i + 1)) / tickStep / this.barWidth) * 100
|
||||
//取整
|
||||
this.endValue = Math.ceil(this.proportion) + ''
|
||||
}
|
||||
if (this.newX == 0) {
|
||||
this.style.setProperty('--dot-move', 0 + 'px')
|
||||
this.endValue = 0 + ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.newX >= this.barWidth) {
|
||||
this.newX = this.barWidth
|
||||
}
|
||||
//改变dot的left值
|
||||
this.style.setProperty('--dot-move', this.newX + 'px')
|
||||
//计算比例
|
||||
this.proportion = (this.newX / this.barWidth) * 100
|
||||
this.pValue = Math.ceil(this.proportion) + ''
|
||||
this.progress.style.setProperty(
|
||||
'width',
|
||||
(this.barWidth * Math.ceil(this.proportion)) / 100 + 'px'
|
||||
)
|
||||
}
|
||||
private touchEnd(evt: TouchEvent) {
|
||||
return console.log(this.pValue)
|
||||
if (this.tick) {
|
||||
return console.log(10 * Math.round(parseInt(this.endValue) / 10) + '')
|
||||
} else {
|
||||
return console.log(this.endValue)
|
||||
}
|
||||
}
|
||||
|
||||
private touchStartVertical(evt: TouchEvent) {
|
||||
if (!this.disabled) {
|
||||
this.startY = evt.touches[0].clientY //手指点下的初始 Y 坐标
|
||||
this.barWidth = this.vSliderBar.offsetHeight //总长度
|
||||
this.dotL = this.barWidth - this.vProgress.offsetTop //黑条长度
|
||||
//按压音量条变宽
|
||||
this.vSliderBar.style.setProperty('--vWidth', '16px')
|
||||
this.vProgress.style.setProperty('--vWidth', '16px')
|
||||
}
|
||||
}
|
||||
private touchMoveVertical(evt: TouchEvent) {
|
||||
if (!this.disabled) {
|
||||
//阻止默认行为
|
||||
evt.preventDefault()
|
||||
this.touchY = evt.touches[0].clientY //整个屏幕实时触摸的 Y 坐标
|
||||
this.moveY = this.startY - this.touchY //手指移动的距离
|
||||
//判断最大值和最小值
|
||||
this.newY = this.dotL + this.moveY
|
||||
if (this.newY < 0) {
|
||||
// console.log('到底了')
|
||||
this.newY = 0
|
||||
}
|
||||
if (this.newY >= this.barWidth) {
|
||||
// console.log('超了')
|
||||
this.newY = this.barWidth
|
||||
}
|
||||
//计算比例
|
||||
this.proportion = (this.newY / this.barWidth) * 100
|
||||
//取整
|
||||
this.endValue = Math.ceil(this.proportion) + ''
|
||||
this.vProgress.style.setProperty('height', this.newY + 'px')
|
||||
}
|
||||
}
|
||||
private touchEndVertical(evt: TouchEvent) {
|
||||
if (!this.disabled) {
|
||||
this.vProgress.style.setProperty('--vWidth', '8px')
|
||||
this.vSliderBar.style.setProperty('--vWidth', '8px')
|
||||
return console.log(this.endValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'star-slider': StarSlider
|
||||
|
|
|
@ -1,29 +1,68 @@
|
|||
import {html, LitElement, css} from 'lit'
|
||||
import {customElement} from 'lit/decorators.js'
|
||||
import {customElement, property} from 'lit/decorators.js'
|
||||
import '../icon/icon'
|
||||
|
||||
@customElement('panel-slider')
|
||||
export class PanelSlider extends LitElement {
|
||||
@property({type: String}) icon = ''
|
||||
|
||||
static styles = css`
|
||||
div {
|
||||
position: relative;
|
||||
border: 1px solid;
|
||||
margin: 50px 50px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
#a {
|
||||
position: absolute;
|
||||
left: 100px;
|
||||
top: 45px;
|
||||
}
|
||||
#b {
|
||||
position: absolute;
|
||||
left: 200px;
|
||||
top: 45px;
|
||||
}
|
||||
`
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div>
|
||||
<h4>default</h4>
|
||||
<star-slider></star-slider>
|
||||
<h4>初始化覆盖长度</h4>
|
||||
<star-slider coverWidth="150px"></star-slider>
|
||||
<star-slider coverWidth="50%"></star-slider>
|
||||
<h4>vertical</h4>
|
||||
<star-slider vertical>
|
||||
<span data-icon="alarm"></span>
|
||||
</star-slider>
|
||||
<star-slider id="a" vertical disabled coverWidth="30%">
|
||||
<span data-icon="bluetooth-a2dp"></span>
|
||||
</star-slider>
|
||||
<star-slider id="b" vertical coverWidth="70%">
|
||||
<span data-icon="alarm-clock"></span>
|
||||
</star-slider>
|
||||
<h4>default - 默认</h4>
|
||||
<star-slider>
|
||||
<p class="left" data-icon="brightness"></p>
|
||||
</star-slider>
|
||||
<h4>coverWidth - 初始覆盖长度</h4>
|
||||
<star-slider coverWidth="150px">
|
||||
<p data-icon="brightness"></p>
|
||||
</star-slider>
|
||||
<star-slider coverWidth="50%">
|
||||
<p data-icon="brightness"></p>
|
||||
</star-slider>
|
||||
<h4>disabled</h4>
|
||||
<star-slider></star-slider>
|
||||
<star-slider></star-slider>
|
||||
<star-slider></star-slider>
|
||||
<star-slider disabled coverWidth="30%"></star-slider>
|
||||
<star-slider disabled coverWidth="70%"></star-slider>
|
||||
<h4>unfilled - 无覆盖</h4>
|
||||
<star-slider unfilled coverWidth="20%"></star-slider>
|
||||
<star-slider unfilled coverWidth="40%"></star-slider>
|
||||
<h4>tick</h4>
|
||||
<h5>step="10"</h5>
|
||||
<star-slider tick step="10"></star-slider>
|
||||
<h5>step="20"</h5>
|
||||
<star-slider tick step="20" coverWidth="20%"></star-slider>
|
||||
<h5>step="50"</h5>
|
||||
<star-slider tick step="50" coverWidth="48%"></star-slider>
|
||||
<h5>step="25"</h5>
|
||||
<star-slider tick step="25" disabled coverWidth="23%"></star-slider>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue