211 lines
5.0 KiB
JavaScript
211 lines
5.0 KiB
JavaScript
/**
|
|
* {进度条}
|
|
* @author chensbox<chensbox@foxmail.com>
|
|
* @date 2023/04/28 16:14:10
|
|
*/
|
|
|
|
import { css, html, Component, styleMap } from 'wkit'
|
|
|
|
const TYPE_LINE = 'line'
|
|
const TYPE_CIRCLE = 'circle'
|
|
const TYPE_DASHBOARD = 'dashboard'
|
|
|
|
const RADIUS = 50 // 半径
|
|
const PERIMETER = 2 * Math.PI * RADIUS // 周长 2*兀*R
|
|
|
|
const OFFSET = { circle: 0, dashboard: (-0.25 * PERIMETER) / 2 + 'px' }
|
|
const DASH_ARRAY = {
|
|
circle: `${PERIMETER}px, ${PERIMETER}px`,
|
|
dashboard: `${PERIMETER * 0.75}px, ${PERIMETER}px`
|
|
}
|
|
|
|
class Progress extends Component {
|
|
static props = {
|
|
value: {
|
|
type: Number,
|
|
default: 0,
|
|
observer(val) {
|
|
this.value = this.#fix(val)
|
|
}
|
|
},
|
|
type: TYPE_LINE
|
|
}
|
|
|
|
static styles = [
|
|
css`
|
|
:host {
|
|
display: flex;
|
|
}
|
|
|
|
.container {
|
|
position: relative;
|
|
width: 100%;
|
|
-webkit-user-select: none;
|
|
user-select: none;
|
|
--line-width: var(--wc-progress-line-width, 6px);
|
|
--base-color: var(--wc-progress-inactive-color, var(--color-plain-2));
|
|
|
|
&[step='1'] {
|
|
--wc-progress-active-color: var(--wc-progress-active-color-1);
|
|
}
|
|
&[step='2'] {
|
|
--wc-progress-active-color: var(--wc-progress-active-color-2);
|
|
}
|
|
&[step='3'] {
|
|
--wc-progress-active-color: var(--wc-progress-active-color-3);
|
|
}
|
|
&[step='4'] {
|
|
--wc-progress-active-color: var(--wc-progress-active-color-4);
|
|
}
|
|
&[step='5'] {
|
|
--wc-progress-active-color: var(--wc-progress-active-color-5);
|
|
}
|
|
}
|
|
`,
|
|
// line
|
|
css`
|
|
:host([type='line']) {
|
|
width: 100%;
|
|
|
|
.progress-value {
|
|
display: none;
|
|
position: absolute;
|
|
z-index: 1;
|
|
top: -32px;
|
|
margin-left: -24px;
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-size: 14px;
|
|
text-align: center;
|
|
background: var(--color-dark-2);
|
|
color: #fff;
|
|
|
|
&::after {
|
|
display: block;
|
|
position: absolute;
|
|
left: 50%;
|
|
margin-left: -3px;
|
|
width: 6px;
|
|
height: 6px;
|
|
background: var(--color-dark-2);
|
|
content: '';
|
|
transform: rotate(45deg);
|
|
}
|
|
}
|
|
|
|
&:host(:hover) {
|
|
.progress-value {
|
|
display: block;
|
|
}
|
|
}
|
|
}
|
|
|
|
.progress-bar {
|
|
display: flex;
|
|
width: 100%;
|
|
height: var(--line-width);
|
|
border-radius: 32px;
|
|
background: var(--base-color);
|
|
|
|
.thumb {
|
|
width: 0;
|
|
height: var(--line-width);
|
|
border-radius: 32px;
|
|
background: var(--wc-progress-active-color, var(--color-teal-1));
|
|
transition: width 0.5s ease;
|
|
}
|
|
}
|
|
`,
|
|
// circle & dashboard
|
|
css`
|
|
:host([type='circle']),
|
|
:host([type='dashboard']) {
|
|
width: var(--wc-progress-size, 128px);
|
|
height: var(--wc-progress-size, 128px);
|
|
|
|
svg {
|
|
color: var(--wc-progress-active-color);
|
|
}
|
|
path {
|
|
fill: none;
|
|
stroke-linecap: round;
|
|
stroke-width: var(--line-width);
|
|
transition: stroke-dasharray 0.5s ease;
|
|
}
|
|
text {
|
|
font-size: calc(var(--line-width) * 4);
|
|
fill: currentColor;
|
|
text-anchor: middle;
|
|
alignment-baseline: middle;
|
|
}
|
|
}
|
|
`
|
|
]
|
|
|
|
get #path() {
|
|
let n = this.type === TYPE_CIRCLE ? -1 : 1
|
|
return `
|
|
M 64 64
|
|
m 0 ${n * RADIUS}
|
|
a ${RADIUS} ${RADIUS} 0 1 1 0 ${-2 * n * RADIUS}
|
|
a ${RADIUS} ${RADIUS} 0 1 1 0 ${2 * n * RADIUS}
|
|
`
|
|
}
|
|
|
|
get #dasharray() {
|
|
let rate = this.type === TYPE_CIRCLE ? 1 : 0.75
|
|
return `${PERIMETER * rate * (this.value / 100)}px, ${PERIMETER}px`
|
|
}
|
|
|
|
#fix(val) {
|
|
return Math.max(0, Math.min(val, 100))
|
|
}
|
|
|
|
#drawCircle() {
|
|
let offset = OFFSET[this.type]
|
|
let dash = DASH_ARRAY[this.type]
|
|
let v = this.value
|
|
return html`
|
|
<svg viewBox="0 0 128 128">
|
|
<path
|
|
d=${this.#path}
|
|
stroke="var(--base-color)"
|
|
stroke-dashoffset=${offset}
|
|
stroke-dasharray=${dash}
|
|
/>
|
|
<path
|
|
d=${this.#path}
|
|
opacity=${v > 0 ? '' : 0}
|
|
stroke="currentColor"
|
|
stroke-dashoffset=${offset}
|
|
stroke-dasharray=${this.#dasharray}
|
|
/>
|
|
<text x="64" y="64">${v}%</text>
|
|
</svg>
|
|
`
|
|
}
|
|
|
|
#drawLine() {
|
|
let v = this.value
|
|
return html`
|
|
<div class="progress-bar">
|
|
<mark class="thumb" style=${styleMap({ width: v + '%' })}></mark>
|
|
</div>
|
|
<div class="progress-value" style=${styleMap({ left: v + '%' })}>
|
|
${v}%
|
|
</div>
|
|
`
|
|
}
|
|
|
|
render() {
|
|
let step = ~~(this.value / 20) || 1
|
|
return html`
|
|
<main class="container" step=${step}>
|
|
${this.type === TYPE_LINE ? this.#drawLine() : this.#drawCircle()}
|
|
</main>
|
|
`
|
|
}
|
|
}
|
|
|
|
Progress.reg('progress')
|
JavaScript
98.9%
CSS
1.1%