完成轮播图组件

master
chenjiajian 2023-04-12 17:19:02 +08:00
parent f94730519a
commit 345b1d1bcc
2 changed files with 167 additions and 35 deletions

View File

@ -49,6 +49,7 @@
- [x] `wc-code`代码高亮插件 - [x] `wc-code`代码高亮插件
- [x] `wc-scroll`滚动组件 - [x] `wc-scroll`滚动组件
- [x] `wc-silder`面包屑组件 - [x] `wc-silder`面包屑组件
- [x] `wc-swipe`轮播图组件
- [x] `wc-breadcrumb`滑块组件 - [x] `wc-breadcrumb`滑块组件
- [ ] `wc-progress`进度条组件 - [ ] `wc-progress`进度条组件
- [ ] `wc-tree`树形菜单组件 - [ ] `wc-tree`树形菜单组件

View File

@ -1,10 +1,10 @@
/** /**
* {} * 轮播图组件
* @author chensbox<chensbox@foxmail.com> * @author chensbox<chensbox@foxmail.com>
* @date 2023/03/26 16:14:10 * @date 2023/03/26 16:14:10
*/ */
import { css, html, Component, nextTick } from '@bd/core' import { css, html, Component, nextTick, styleMap } from '@bd/core'
import '../icon/index.js' import '../icon/index.js'
const CARD_SCALE = 0.83 const CARD_SCALE = 0.83
function createWatcher(object, key, effect) { function createWatcher(object, key, effect) {
@ -19,7 +19,7 @@ function createWatcher(object, key, effect) {
} }
}) })
} }
class Carousel extends Component { class Swipe extends Component {
static props = { static props = {
'initial-index': { 'initial-index': {
type: Number, type: Number,
@ -34,7 +34,7 @@ class Carousel extends Component {
}, },
trigger: { trigger: {
type: String, type: String,
default: 'hover' default: 'hover' // hover or click
}, },
autoplay: { autoplay: {
type: Boolean, type: Boolean,
@ -47,23 +47,26 @@ class Carousel extends Component {
type: Number, type: Number,
default: 3000 default: 3000
}, },
'indicator-position': { String, attributes: false },
indicator: { indicator: {
type: Boolean, type: Boolean,
default: true default: true
}, },
arrow: { arrow: {
type: String, type: String,
default: 'hover' default: 'hover' // hover or alway
},
type: {
type: String,
default: '', // '' or card
attributes: false
}, },
type: { type: String, default: '', attributes: false },
loop: { loop: {
type: Boolean, type: Boolean,
default: true default: true
}, },
direction: { direction: {
type: String, type: String,
default: 'horizontal' default: 'horizontal' // horizontal or vertical
}, },
'pause-on-hover': { 'pause-on-hover': {
type: Boolean, type: Boolean,
@ -77,7 +80,34 @@ class Carousel extends Component {
display: block; display: block;
width: 100%; width: 100%;
} }
:host([direction='vertical']) {
.indicator {
flex-direction: column;
left: calc(100% - 26px);
top: 50%;
height: 80%;
width: 26px;
transform: translatey(-50%);
li {
width: 26px;
height: 24px;
margin: 5px 0;
&::after {
position: absolute;
content: '';
left: calc(50% - 2px);
top: 0;
height: 100%;
width: 2px;
background-color: #fff;
opacity: 0.48;
}
&.active::after {
opacity: 1;
}
}
}
}
.toggle-btn { .toggle-btn {
z-index: 4; z-index: 4;
user-select: none; user-select: none;
@ -86,7 +116,7 @@ class Carousel extends Component {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
opacity: 0.8; opacity: 0;
width: 33px; width: 33px;
height: 33px; height: 33px;
font-size: 24px; font-size: 24px;
@ -95,6 +125,11 @@ class Carousel extends Component {
border-color: #fff; border-color: #fff;
border-radius: 50%; border-radius: 50%;
--size: 12px; --size: 12px;
transition: opacity linear 0.2s;
&:hover {
opacity: 0.8 !important;
}
} }
.left, .left,
.right { .right {
@ -102,10 +137,43 @@ class Carousel extends Component {
transform: translateY(-50%); transform: translateY(-50%);
} }
.left { .left {
left: 5%; left: 3%;
} }
.right { .right {
right: 5%; right: 3%;
}
.indicator {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 80%;
bottom: 0px;
left: 50%;
transform: translateX(-50%);
li {
position: relative;
width: 24px;
height: 26px;
margin: 0 5px;
list-style: none;
&::after {
position: absolute;
content: '';
left: 0;
top: calc(50% - 2px);
width: 100%;
height: 2px;
background-color: #fff;
opacity: 0.48;
}
&:hover {
cursor: pointer;
}
&.active::after {
opacity: 1;
}
}
} }
` `
items = [] items = []
@ -114,7 +182,7 @@ class Carousel extends Component {
updateItems() { updateItems() {
this.items = Array.from(this.children).filter( this.items = Array.from(this.children).filter(
item => item.tagName === 'WC-CAROUSEL-ITEM' item => item.tagName === 'WC-SWIPE-ITEM'
) )
nextTick(() => this.$requestUpdate()) nextTick(() => this.$requestUpdate())
} }
@ -147,6 +215,7 @@ class Carousel extends Component {
this.items.forEach((item, index) => { this.items.forEach((item, index) => {
item.translateItem(index, this.activeIndex, oldIndex) item.translateItem(index, this.activeIndex, oldIndex)
}) })
nextTick(() => this.$requestUpdate())
} }
setActiveItem(index) { setActiveItem(index) {
const now = Date.now() const now = Date.now()
@ -176,12 +245,18 @@ class Carousel extends Component {
if (oldIndex !== this.activeIndex) { if (oldIndex !== this.activeIndex) {
this.resetItemPosition(oldIndex) this.resetItemPosition(oldIndex)
} }
// this.resetItemPosition(oldIndex)
this.resetTimer() this.resetTimer()
nextTick(() => this.$requestUpdate())
} }
handleIndicatorHover(index) { handleIndicatorHover(event) {
let index = +event.target.getAttribute('index')
if (this.trigger === 'hover' && index !== this.activeIndex) { if (this.trigger === 'hover' && index !== this.activeIndex) {
// this.activeIndex = index this.setActiveItem(index)
}
}
handleClickIndicator(event) {
let index = +event.target.getAttribute('index')
if (this.trigger === 'click' && index !== this.activeIndex) {
this.setActiveItem(index) this.setActiveItem(index)
} }
} }
@ -191,6 +266,18 @@ class Carousel extends Component {
next() { next() {
this.setActiveItem(this.activeIndex + 1) this.setActiveItem(this.activeIndex + 1)
} }
showArrow() {
this.showArrowBtn = true
this.$requestUpdate()
}
hideArrow() {
this.showArrowBtn = false
this.$requestUpdate()
}
handleSlotChange() {
this.updateItems()
this.resetItemPosition()
}
mounted() { mounted() {
this.updateItems() this.updateItems()
nextTick(() => { nextTick(() => {
@ -202,6 +289,18 @@ class Carousel extends Component {
this.resizeObserve.observe(this) this.resizeObserve.observe(this)
this.startTimer() this.startTimer()
}) })
if (this['pause-on-hover']) {
this.$on('mouseenter', () => this.pauseTimer())
this.$on('mouseleave', () => this.startTimer())
}
if (this.arrow === 'hover') {
this.$on('mouseenter', () => this.showArrow())
this.$on('mouseleave', () => this.hideArrow())
} else if (this.arrow === 'alway') {
this.showArrowBtn = true
this.$requestUpdate()
}
} }
unmounted() { unmounted() {
this.resizeObserve.disconnect() this.resizeObserve.disconnect()
@ -209,23 +308,46 @@ class Carousel extends Component {
this.pauseTimer() this.pauseTimer()
} }
render() { render() {
let styles = styleMap({
opacity: this.showArrowBtn ? 0.5 : 0
})
let toggleBtns =
this.direction === 'horizontal'
? html`
<div class="left toggle-btn" style=${styles} @click=${this.prev}>
<wc-icon name="left"></wc-icon>
</div>
<div class="right toggle-btn" style=${styles} @click=${this.next}>
<wc-icon name="right"></wc-icon>
</div>
`
: ''
let indicator = this.indicator
? html`
<ul class="indicator" @click=${this.handleClickIndicator}>
${this.items.map(
(item, index) =>
html` <li
index=${index}
class=${this.activeIndex === index ? 'active' : ''}
@mouseenter=${this.handleIndicatorHover}
></li>`
)}
</ul>
`
: ''
return html` return html`
<div class="container" style=${`height:${this.height}px`}> <div class="container" style=${`height:${this.height}px`}>
<slot></slot> <slot @slotchange=${this.handleSlotChange}></slot>
<div class="left toggle-btn" @click=${this.prev}> ${toggleBtns}
<wc-icon name="left"></wc-icon>
</div>
<div class="right toggle-btn" @click=${this.next}>
<wc-icon name="right"></wc-icon>
</div>
</div> </div>
<ul> ${indicator}
${this.items.map(_ => html`<li>3</li>`)}
</ul>
` `
} }
} }
class CarouselItem extends Component { class SwipeItem extends Component {
static props = { static props = {
name: { String, attributes: false } name: { String, attributes: false }
} }
@ -248,7 +370,8 @@ class CarouselItem extends Component {
:host([card]) { :host([card]) {
width: 50%; width: 50%;
transition-duration: 400ms; transition-duration: 400ms;
transition-property: transform, opacity; transition-property: transform, opacity, filter;
cursor: pointer;
} }
` `
static watch = { static watch = {
@ -269,16 +392,22 @@ class CarouselItem extends Component {
if (this.$parent.type !== 'card') { if (this.$parent.type !== 'card') {
return return
} }
this.style.opacity = 1 let style = {
opacity: 1,
'z-index': 1,
filter: 'brightness(0.8)'
}
if (this.inStage) { if (this.inStage) {
this.style['z-index'] = 1 style['z-index'] = 1
} else { } else {
this.style['z-index'] = -1 style['z-index'] = -1
this.style.opacity = 0 style.opacity = 0
} }
if (val) { if (val) {
this.style['z-index'] = 2 style['z-index'] = 2
style.filter = 'brightness(1)'
} }
Object.assign(this.style, style)
} }
} }
processIndex(index, activeIndex, length) { processIndex(index, activeIndex, length) {
@ -347,9 +476,11 @@ class CarouselItem extends Component {
this.$parent = this.parentNode this.$parent = this.parentNode
if (this.$parent.type === 'card') { if (this.$parent.type === 'card') {
this.setAttribute('card', '') this.setAttribute('card', '')
const index = this.$parent.items.indexOf(this)
this.$on('click', () => this.$parent.setActiveItem(index))
} }
} }
} }
Carousel.reg('carousel') Swipe.reg('swipe')
CarouselItem.reg('carousel-item') SwipeItem.reg('swipe-item')