增加轮播图组件
parent
8d9c42b019
commit
69714015c9
|
@ -23,10 +23,12 @@
|
||||||
- [ ] `wc-collapse`折叠组件
|
- [ ] `wc-collapse`折叠组件
|
||||||
- [ ] `wc-counter`倒计时组件
|
- [ ] `wc-counter`倒计时组件
|
||||||
- [ ] `wc-drag`拖拽组件
|
- [ ] `wc-drag`拖拽组件
|
||||||
|
- [x] `wc-image`图片组件
|
||||||
|
- [x] `wc-image-preview`图片预览组件
|
||||||
- [x] `wc-button`表单组件-按钮
|
- [x] `wc-button`表单组件-按钮
|
||||||
- [x] `wc-link`表单组件-链接按钮
|
- [x] `wc-link`表单组件-链接按钮
|
||||||
- [x] `wc-checkbox`表单组件-复选框
|
- [x] `wc-checkbox`表单组件-复选框
|
||||||
- [ ] `wc-input`表单组件-文本输入框
|
- [x] `wc-input`表单组件-文本输入框
|
||||||
- [x] `wc-passwd`表单组件-文本输入框
|
- [x] `wc-passwd`表单组件-文本输入框
|
||||||
- [x] `wc-textarea`表单组件-多行文本输入框
|
- [x] `wc-textarea`表单组件-多行文本输入框
|
||||||
- [x] `wc-number`表单组件-步进数字输入
|
- [x] `wc-number`表单组件-步进数字输入
|
||||||
|
|
|
@ -0,0 +1,355 @@
|
||||||
|
/**
|
||||||
|
* {}
|
||||||
|
* @author chensbox<chensbox@foxmail.com>
|
||||||
|
* @date 2023/03/26 16:14:10
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { css, html, Component, nextTick } from '@bd/core'
|
||||||
|
import '../icon/index.js'
|
||||||
|
const CARD_SCALE = 0.83
|
||||||
|
function createWatcher(object, key, effect) {
|
||||||
|
let value
|
||||||
|
Object.defineProperty(object, key, {
|
||||||
|
get() {
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
value = val
|
||||||
|
effect.call(object, val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
class Carousel extends Component {
|
||||||
|
static props = {
|
||||||
|
'initial-index': {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: 200,
|
||||||
|
observer(val) {
|
||||||
|
this.style.height = `${val}px`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: {
|
||||||
|
type: String,
|
||||||
|
default: 'hover'
|
||||||
|
},
|
||||||
|
autoplay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
observer(val) {
|
||||||
|
val ? this.startTimer() : this.pauseTimer()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interval: {
|
||||||
|
type: Number,
|
||||||
|
default: 3000
|
||||||
|
},
|
||||||
|
'indicator-position': { String, attributes: false },
|
||||||
|
indicator: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
type: String,
|
||||||
|
default: 'hover'
|
||||||
|
},
|
||||||
|
type: { type: String, default: '', attributes: false },
|
||||||
|
loop: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
direction: {
|
||||||
|
type: String,
|
||||||
|
default: 'horizontal'
|
||||||
|
},
|
||||||
|
'pause-on-hover': {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-btn {
|
||||||
|
z-index: 4;
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0.8;
|
||||||
|
width: 33px;
|
||||||
|
height: 33px;
|
||||||
|
font-size: 24px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #606266;
|
||||||
|
border-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
--size: 12px;
|
||||||
|
}
|
||||||
|
.left,
|
||||||
|
.right {
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
left: 5%;
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
right: 5%;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
items = []
|
||||||
|
activeIndex = 0
|
||||||
|
stamp = 0
|
||||||
|
|
||||||
|
updateItems() {
|
||||||
|
this.items = Array.from(this.children).filter(
|
||||||
|
item => item.tagName === 'WC-CAROUSEL-ITEM'
|
||||||
|
)
|
||||||
|
nextTick(() => this.$requestUpdate())
|
||||||
|
}
|
||||||
|
playSlides() {
|
||||||
|
let oldIndex = this.activeIndex
|
||||||
|
if (this.activeIndex < this.items.length - 1) {
|
||||||
|
this.activeIndex++
|
||||||
|
} else if (this.loop) {
|
||||||
|
this.activeIndex = 0
|
||||||
|
}
|
||||||
|
this.resetItemPosition(oldIndex)
|
||||||
|
}
|
||||||
|
startTimer() {
|
||||||
|
if (!this.interval || this.interval <= 0 || !this.autoplay || this.timer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.timer = setInterval(this.playSlides.bind(this), this.interval)
|
||||||
|
}
|
||||||
|
pauseTimer() {
|
||||||
|
if (this.timer) {
|
||||||
|
clearInterval(this.timer)
|
||||||
|
this.timer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetTimer() {
|
||||||
|
this.pauseTimer()
|
||||||
|
this.startTimer()
|
||||||
|
}
|
||||||
|
resetItemPosition(oldIndex) {
|
||||||
|
this.items.forEach((item, index) => {
|
||||||
|
item.translateItem(index, this.activeIndex, oldIndex)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setActiveItem(index) {
|
||||||
|
const now = Date.now()
|
||||||
|
if (now - this.stamp < 300) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.stamp = now
|
||||||
|
if (typeof index === 'string') {
|
||||||
|
const filteredItems = this.items.filter(item => item.name === index)
|
||||||
|
if (filteredItems.length > 0) {
|
||||||
|
index = this.items.indexOf(filteredItems[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index = Number(index)
|
||||||
|
if (isNaN(index) || index !== Math.floor(index)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const length = this.items.length
|
||||||
|
const oldIndex = this.activeIndex
|
||||||
|
if (index < 0) {
|
||||||
|
this.activeIndex = this.loop ? length - 1 : 0
|
||||||
|
} else if (index >= length) {
|
||||||
|
this.activeIndex = this.loop ? 0 : length - 1
|
||||||
|
} else {
|
||||||
|
this.activeIndex = index
|
||||||
|
}
|
||||||
|
if (oldIndex !== this.activeIndex) {
|
||||||
|
this.resetItemPosition(oldIndex)
|
||||||
|
}
|
||||||
|
// this.resetItemPosition(oldIndex)
|
||||||
|
this.resetTimer()
|
||||||
|
}
|
||||||
|
handleIndicatorHover(index) {
|
||||||
|
if (this.trigger === 'hover' && index !== this.activeIndex) {
|
||||||
|
// this.activeIndex = index
|
||||||
|
this.setActiveItem(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev() {
|
||||||
|
this.setActiveItem(this.activeIndex - 1)
|
||||||
|
}
|
||||||
|
next() {
|
||||||
|
this.setActiveItem(this.activeIndex + 1)
|
||||||
|
}
|
||||||
|
mounted() {
|
||||||
|
this.updateItems()
|
||||||
|
nextTick(() => {
|
||||||
|
let initialIndex = this['initial-index']
|
||||||
|
if (initialIndex >= 0 && initialIndex < this.items.length) {
|
||||||
|
this.activeIndex = initialIndex
|
||||||
|
}
|
||||||
|
this.resizeObserve = new ResizeObserver(this.resetItemPosition.bind(this))
|
||||||
|
this.resizeObserve.observe(this)
|
||||||
|
this.startTimer()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
unmounted() {
|
||||||
|
this.resizeObserve.disconnect()
|
||||||
|
this.resizeObserve = null
|
||||||
|
this.pauseTimer()
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div class="container" style=${`height:${this.height}px`}>
|
||||||
|
<slot></slot>
|
||||||
|
<div class="left toggle-btn" @click=${this.prev}>
|
||||||
|
<wc-icon name="left"></wc-icon>
|
||||||
|
</div>
|
||||||
|
<div class="right toggle-btn" @click=${this.next}>
|
||||||
|
<wc-icon name="right"></wc-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul>
|
||||||
|
${this.items.map(_ => html`<li>3</li>`)}
|
||||||
|
</ul>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CarouselItem extends Component {
|
||||||
|
static props = {
|
||||||
|
name: { String, attributes: false }
|
||||||
|
}
|
||||||
|
static styles = css`
|
||||||
|
:host {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
display: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
transition: transform ease-in-out 0s;
|
||||||
|
}
|
||||||
|
:host([ready]) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
:host([animating]) {
|
||||||
|
transition-duration: 400ms;
|
||||||
|
}
|
||||||
|
:host([card]) {
|
||||||
|
width: 50%;
|
||||||
|
transition-duration: 400ms;
|
||||||
|
transition-property: transform, opacity;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
static watch = {
|
||||||
|
translate(val) {
|
||||||
|
const translateType =
|
||||||
|
this.$parent.direction === 'horizontal' ? 'translateX' : 'translateY'
|
||||||
|
this.style.transform = `${translateType}(${val}px) scale(${this.scale})`
|
||||||
|
},
|
||||||
|
ready(val) {
|
||||||
|
val ? this.setAttribute('ready', '') : this.removeAttribute('ready')
|
||||||
|
},
|
||||||
|
animating(val) {
|
||||||
|
val
|
||||||
|
? this.setAttribute('animating', '')
|
||||||
|
: this.removeAttribute('animating')
|
||||||
|
},
|
||||||
|
active(val) {
|
||||||
|
if (this.$parent.type !== 'card') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.style.opacity = 1
|
||||||
|
if (this.inStage) {
|
||||||
|
this.style['z-index'] = 1
|
||||||
|
} else {
|
||||||
|
this.style['z-index'] = -1
|
||||||
|
this.style.opacity = 0
|
||||||
|
}
|
||||||
|
if (val) {
|
||||||
|
this.style['z-index'] = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processIndex(index, activeIndex, length) {
|
||||||
|
if (activeIndex === 0 && index === length - 1) {
|
||||||
|
return -1
|
||||||
|
} else if (activeIndex === length - 1 && index === 0) {
|
||||||
|
return length
|
||||||
|
} else if (index < activeIndex - 1 && activeIndex - index >= length / 2) {
|
||||||
|
return length + 1
|
||||||
|
} else if (index > activeIndex + 1 && index - activeIndex >= length / 2) {
|
||||||
|
return -2
|
||||||
|
}
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
calcCardTranslate(index, activeIndex) {
|
||||||
|
const parentWidth = this.$parent.offsetWidth
|
||||||
|
if (this.inStage) {
|
||||||
|
return (parentWidth * ((2 - CARD_SCALE) * (index - activeIndex) + 1)) / 4
|
||||||
|
} else if (index < activeIndex) {
|
||||||
|
return (-(1 + CARD_SCALE) * parentWidth) / 4
|
||||||
|
} else {
|
||||||
|
return ((3 + CARD_SCALE) * parentWidth) / 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
calcTranslate(index, activeIndex, isVertical) {
|
||||||
|
const distance = this.$parent[isVertical ? 'offsetHeight' : 'offsetWidth']
|
||||||
|
return distance * (index - activeIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
translateItem(index, activeIndex, oldIndex) {
|
||||||
|
const parentType = this.$parent.type
|
||||||
|
const parentDirection = this.$parent.direction
|
||||||
|
const length = this.$parent.items.length
|
||||||
|
if (parentType !== 'card' && oldIndex !== undefined) {
|
||||||
|
this.animating = index === activeIndex || index === oldIndex
|
||||||
|
}
|
||||||
|
if (index !== activeIndex && length > 2 && this.$parent.loop) {
|
||||||
|
index = this.processIndex(index, activeIndex, length)
|
||||||
|
}
|
||||||
|
if (parentType === 'card') {
|
||||||
|
if (parentDirection === 'vertical') {
|
||||||
|
console.warn(
|
||||||
|
'[Warn][Carousel]vertical direction is not supported in card mode'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.inStage = Math.round(Math.abs(index - activeIndex)) <= 1
|
||||||
|
this.active = index === activeIndex
|
||||||
|
this.scale = this.active ? 1 : CARD_SCALE
|
||||||
|
this.animating = true
|
||||||
|
this.translate = this.calcCardTranslate(index, activeIndex)
|
||||||
|
} else {
|
||||||
|
this.active = index === activeIndex
|
||||||
|
const isVertical = parentDirection === 'vertical'
|
||||||
|
this.scale = 1
|
||||||
|
this.translate = this.calcTranslate(index, activeIndex, isVertical)
|
||||||
|
}
|
||||||
|
!this.ready && (this.ready = true)
|
||||||
|
}
|
||||||
|
created() {
|
||||||
|
const { watch } = this.constructor
|
||||||
|
for (const key in watch) {
|
||||||
|
createWatcher(this, key, watch[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mounted() {
|
||||||
|
this.$parent = this.parentNode
|
||||||
|
if (this.$parent.type === 'card') {
|
||||||
|
this.setAttribute('card', '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Carousel.reg('carousel')
|
||||||
|
CarouselItem.reg('carousel-item')
|
Loading…
Reference in New Issue