diff --git a/Readme.md b/Readme.md index 875a91e..47a2d52 100644 --- a/Readme.md +++ b/Readme.md @@ -23,10 +23,12 @@ - [ ] `wc-collapse`折叠组件 - [ ] `wc-counter`倒计时组件 - [ ] `wc-drag`拖拽组件 +- [x] `wc-image`图片组件 +- [x] `wc-image-preview`图片预览组件 - [x] `wc-button`表单组件-按钮 - [x] `wc-link`表单组件-链接按钮 - [x] `wc-checkbox`表单组件-复选框 -- [ ] `wc-input`表单组件-文本输入框 +- [x] `wc-input`表单组件-文本输入框 - [x] `wc-passwd`表单组件-文本输入框 - [x] `wc-textarea`表单组件-多行文本输入框 - [x] `wc-number`表单组件-步进数字输入 diff --git a/src/carousel/index.js b/src/carousel/index.js new file mode 100644 index 0000000..389ab75 --- /dev/null +++ b/src/carousel/index.js @@ -0,0 +1,355 @@ +/** + * {} + * @author chensbox + * @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` +
+ +
+ +
+
+ +
+
+ + ` + } +} +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')