Commit e7f1cf287711cfba9b5f0ecf853ba0d02cdea9f0

Authored by Aresn
Committed by GitHub
2 parents 6bfdf8f8 53e38f9c

Merge pull request #218 from rijn/carousel

Carousel
src/components/carousel/carousel-item.vue 0 → 100644
  1 +<template>
  2 + <div :class="prefixCls" v-bind:style="styles"><slot></slot></div>
  3 +</template>
  4 +<script>
  5 + const prefixCls = 'ivu-carousel-item';
  6 +
  7 + export default {
  8 + componentName: 'carousel-item',
  9 +
  10 + data () {
  11 + return {
  12 + prefixCls: prefixCls,
  13 + width: 0,
  14 + height: 'auto',
  15 + left: 0
  16 + };
  17 + },
  18 + computed: {
  19 + styles () {
  20 + return {
  21 + width: `${this.width}px`,
  22 + height: `${this.height}`,
  23 + left: `${this.left}px`
  24 + };
  25 + }
  26 + },
  27 + compiled () {
  28 + this.$parent.slotChange();
  29 + },
  30 + beforeDestroy () {
  31 + this.$parent.slotChange();
  32 + }
  33 + };
  34 +</script>
... ...
src/components/carousel/carousel.vue 0 → 100644
  1 +<template>
  2 + <div :class="classes">
  3 + <button :class="arrowClasses" class="left" @click="add(-1)">
  4 + <Icon type="chevron-left"></Icon>
  5 + </button>
  6 + <div :class="[prefixCls + '-list']">
  7 + <div :class="[prefixCls + '-track']" :style="trackStyles" v-el:slides>
  8 + <slot></slot>
  9 + </div>
  10 + </div>
  11 + <button :class="arrowClasses" class="right" @click="add(1)">
  12 + <Icon type="chevron-right"></Icon>
  13 + </button>
  14 + <ul :class="dotsClasses">
  15 + <template v-for="n in slides.length">
  16 + <li :class="{ [`${prefixCls}-active`]: n === currentIndex }"
  17 + @click="dotsEvent('click', n)"
  18 + @mouseover="dotsEvent('hover', n)">
  19 + <button></button>
  20 + </li>
  21 + </template>
  22 + </ul>
  23 + </div>
  24 +</template>
  25 +<script>
  26 + import Icon from '../icon/icon.vue';
  27 + import { getStyle, oneOf } from '../../utils/assist';
  28 +
  29 + const prefixCls = 'ivu-carousel';
  30 +
  31 + export default {
  32 + name: 'Carousel',
  33 + components: { Icon },
  34 + props: {
  35 + arrow: {
  36 + type: String,
  37 + default: 'hover',
  38 + validator (value) {
  39 + return oneOf(value, ['hover', 'always', 'never']);
  40 + }
  41 + },
  42 + autoplay: {
  43 + type: Boolean,
  44 + default: true
  45 + },
  46 + autoplaySpeed: {
  47 + type: Number,
  48 + default: 2000
  49 + },
  50 + easing: {
  51 + type: String,
  52 + default: 'ease'
  53 + },
  54 + dots: {
  55 + type: String,
  56 + default: 'inside',
  57 + validator (value) {
  58 + return oneOf(value, ['inside', 'outside', 'none']);
  59 + }
  60 + },
  61 + trigger: {
  62 + type: String,
  63 + default: 'click',
  64 + validator (value) {
  65 + return oneOf(value, ['click', 'hover']);
  66 + }
  67 + },
  68 + currentIndex: {
  69 + type: Number,
  70 + default: 0
  71 + },
  72 + height: {
  73 + type: [String, Number],
  74 + default: 'auto',
  75 + validator (value) {
  76 + return value === 'auto' || toString.call(value) === '[object Number]';
  77 + }
  78 + }
  79 + },
  80 + data () {
  81 + return {
  82 + prefixCls: prefixCls,
  83 + listWidth: 0,
  84 + trackWidth: 0,
  85 + trackOffset: 0,
  86 + slides: [],
  87 + slideInstances: [],
  88 + timer: null,
  89 + ready: false
  90 + };
  91 + },
  92 + computed: {
  93 + classes () {
  94 + return [
  95 + `${prefixCls}`
  96 + ];
  97 + },
  98 + trackStyles () {
  99 + return {
  100 + width: `${this.trackWidth}px`,
  101 + transform: `translate3d(-${this.trackOffset}px, 0px, 0px)`,
  102 + transition: `transform 500ms ${this.easing}`
  103 + };
  104 + },
  105 + arrowClasses () {
  106 + return [
  107 + `${prefixCls}-arrow`,
  108 + `${prefixCls}-arrow-${this.arrow}`
  109 + ];
  110 + },
  111 + dotsClasses () {
  112 + return [
  113 + `${prefixCls}-dots`,
  114 + `${prefixCls}-dots-${this.dots}`
  115 + ];
  116 + }
  117 + },
  118 + methods: {
  119 + // find option component
  120 + findChild (cb) {
  121 + const find = function (child) {
  122 + const name = child.$options.componentName;
  123 +
  124 + if (name) {
  125 + cb(child);
  126 + } else if (child.$children.length) {
  127 + child.$children.forEach((innerChild) => {
  128 + find(innerChild, cb);
  129 + });
  130 + }
  131 + };
  132 +
  133 + if (this.slideInstances.length || !this.$children) {
  134 + this.slideInstances.forEach((child) => {
  135 + find(child);
  136 + });
  137 + } else {
  138 + this.$children.forEach((child) => {
  139 + find(child);
  140 + });
  141 + }
  142 + },
  143 + updateSlides (init ) {
  144 + let slides = [];
  145 + let index = 1;
  146 +
  147 + this.findChild((child) => {
  148 + slides.push({
  149 + $el: child.$el
  150 + });
  151 + child.index = index++;
  152 +
  153 + if (init) {
  154 + this.slideInstances.push(child);
  155 + }
  156 + });
  157 +
  158 + this.slides = slides;
  159 +
  160 + this.updatePos();
  161 + },
  162 + updatePos () {
  163 + this.findChild((child) => {
  164 + child.width = this.listWidth;
  165 + child.height = typeof this.height === 'number' ? `${this.height}px` : this.height;
  166 + });
  167 +
  168 + this.trackWidth = (this.slides.length || 0) * this.listWidth;
  169 + },
  170 + // use when slot changed
  171 + slotChange () {
  172 + this.$nextTick(() => {
  173 + this.slides = [];
  174 + this.slideInstances = [];
  175 +
  176 + this.updateSlides(true, true);
  177 + this.updatePos();
  178 + });
  179 + },
  180 + handleResize () {
  181 + this.listWidth = parseInt(getStyle(this.$el, 'width'));
  182 + this.updatePos();
  183 + },
  184 + add (offset) {
  185 + let index = this.currentIndex;
  186 + index += offset;
  187 + while (index < 0) index += this.slides.length;
  188 + index = index % this.slides.length;
  189 + this.currentIndex = index;
  190 + },
  191 + dotsEvent (event, n) {
  192 + if (event === this.trigger && this.currentIndex !== n) {
  193 + this.currentIndex = n;
  194 + }
  195 + },
  196 + setAutoplay () {
  197 + window.clearInterval(this.timer);
  198 + if (this.autoplay) {
  199 + this.timer = window.setInterval(() => {
  200 + this.add(1);
  201 + }, this.autoplaySpeed);
  202 + }
  203 + },
  204 + updateOffset () {
  205 + this.$nextTick(() => {
  206 + this.handleResize();
  207 + this.trackOffset = this.currentIndex * this.listWidth;
  208 + });
  209 + }
  210 + },
  211 + compiled () {
  212 + this.updateSlides(true);
  213 + },
  214 + watch: {
  215 + autoplay () {
  216 + this.setAutoplay();
  217 + },
  218 + autoplaySpeed () {
  219 + this.setAutoplay();
  220 + },
  221 + currentIndex (val, oldVal) {
  222 + this.$emit('on-change', oldVal, val);
  223 + this.updateOffset();
  224 + },
  225 + height () {
  226 + this.updatePos();
  227 + }
  228 + },
  229 + ready () {
  230 + this.handleResize();
  231 + this.setAutoplay();
  232 + window.addEventListener('resize', this.handleResize, false);
  233 + },
  234 + beforeDestroy () {
  235 + window.removeEventListener('resize', this.handleResize, false);
  236 + }
  237 + };
  238 +</script>
... ...
src/components/carousel/index.js 0 → 100644
  1 +import Carousel from './carousel.vue';
  2 +import CarouselItem from './carousel-item.vue';
  3 +
  4 +Carousel.Item = CarouselItem;
  5 +export default Carousel;
0 6 \ No newline at end of file
... ...
src/index.js
... ... @@ -8,6 +8,7 @@ import Badge from &#39;./components/badge&#39;;
8 8 import Breadcrumb from './components/breadcrumb';
9 9 import Button from './components/button';
10 10 import Card from './components/card';
  11 +import Carousel from './components/carousel';
11 12 import Cascader from './components/cascader';
12 13 import Checkbox from './components/checkbox';
13 14 import Circle from './components/circle';
... ... @@ -53,6 +54,8 @@ const iview = {
53 54 iButton: Button,
54 55 ButtonGroup: Button.Group,
55 56 Card,
  57 + Carousel,
  58 + CarouselItem: Carousel.Item,
56 59 Cascader,
57 60 Checkbox,
58 61 CheckboxGroup: Checkbox.Group,
... ...
src/styles/components/carousel.less 0 → 100644
  1 +@carousel-prefix-cls: ~"@{css-prefix}carousel";
  2 +@carousel-item-prefix-cls: ~"@{css-prefix}carousel-item";
  3 +
  4 +.@{carousel-prefix-cls} {
  5 + position: relative;
  6 + display: block;
  7 + box-sizing: border-box;
  8 + user-select: none;
  9 + touch-action: pan-y;
  10 + -webkit-tap-highlight-color: transparent;
  11 +
  12 + &-track, &-list {
  13 + transform: translate3d(0, 0, 0);
  14 + }
  15 +
  16 + &-list {
  17 + position: relative;
  18 + display: block;
  19 + overflow: hidden;
  20 +
  21 + margin: 0;
  22 + padding: 0;
  23 + }
  24 +
  25 + &-track {
  26 + position: relative;
  27 + top: 0;
  28 + left: 0;
  29 + display: block;
  30 +
  31 + overflow: hidden;
  32 +
  33 + z-index: 1;
  34 + }
  35 +
  36 + &-item {
  37 + float: left;
  38 + height: 100%;
  39 + min-height: 1px;
  40 + display: block;
  41 + }
  42 +
  43 + &-arrow {
  44 +
  45 + border: none;
  46 + outline: none;
  47 +
  48 + padding: 0;
  49 + margin: 0;
  50 +
  51 + width: 36px;
  52 + height: 36px;
  53 + border-radius: 50%;
  54 +
  55 + cursor: pointer;
  56 +
  57 + display: none;
  58 +
  59 + position: absolute;
  60 + top: 50%;
  61 + z-index: 10;
  62 + transform: translateY(-50%);
  63 +
  64 + transition: .3s;
  65 + background-color: rgba(31, 45, 61, .11);
  66 + color: #fff;
  67 +
  68 + &:hover {
  69 + background-color: rgba(31, 45, 61, 0.5);
  70 + }
  71 +
  72 + text-align: center;
  73 + font-size: 1em;
  74 +
  75 + font-family: inherit;
  76 + line-height: inherit;
  77 +
  78 + & > * {
  79 + vertical-align: baseline;
  80 + }
  81 +
  82 + &.left {
  83 + left: 16px;
  84 + }
  85 +
  86 + &.right {
  87 + right: 16px;
  88 + }
  89 +
  90 + &-always {
  91 + display: inherit;
  92 + }
  93 +
  94 + &-hover {
  95 + display: inherit;
  96 +
  97 + opacity: 0;
  98 + }
  99 + }
  100 +
  101 + &:hover &-arrow-hover {
  102 + opacity: 1;
  103 + }
  104 +
  105 + &-dots {
  106 + z-index: 10;
  107 +
  108 + @padding: 7px;
  109 +
  110 + display: none;
  111 +
  112 + position: relative;
  113 + &-inside {
  114 + display: block;
  115 + position: absolute;
  116 + bottom: 10px - @padding;
  117 + }
  118 +
  119 + &-outside {
  120 + display: block;
  121 + margin-top: 10px - @padding;
  122 + }
  123 +
  124 + list-style: none;
  125 +
  126 + text-align: center;
  127 +
  128 + padding: 0;
  129 + width: 100%;
  130 + height: 3px + @padding * 2;
  131 +
  132 + li {
  133 + position: relative;
  134 + display: inline-block;
  135 +
  136 + vertical-align: top;
  137 + text-align: center;
  138 +
  139 + margin: 0 2px;
  140 + padding: @padding 0;
  141 +
  142 + cursor: pointer;
  143 +
  144 + button {
  145 + border: 0;
  146 + cursor: pointer;
  147 +
  148 + background: #8391a5;
  149 + opacity: 0.3;
  150 +
  151 + display: block;
  152 + width: 16px;
  153 + height: 3px;
  154 +
  155 + border-radius: 1px;
  156 + outline: none;
  157 +
  158 + font-size: 0;
  159 + color: transparent;
  160 +
  161 + -webkit-transition: all .5s;
  162 + transition: all .5s;
  163 + }
  164 +
  165 + &:hover > button {
  166 + opacity: 0.7;
  167 + }
  168 +
  169 + &.@{carousel-prefix-cls}-active > button {
  170 + opacity: 1;
  171 + width: 24px;
  172 + }
  173 + }
  174 + }
  175 +}
... ...
src/styles/components/index.less
... ... @@ -35,4 +35,5 @@
35 35 @import "date-picker";
36 36 @import "time-picker";
37 37 @import "form";
38   -@import "rate";
39 38 \ No newline at end of file
  39 +@import "carousel";
  40 +@import "rate";
... ...
test/app.vue
... ... @@ -47,6 +47,7 @@ li + li {
47 47 <li><a v-link="'/menu'">Menu</a></li>
48 48 <li><a v-link="'/date'">Date</a></li>
49 49 <li><a v-link="'/form'">Form</a></li>
  50 + <li><a v-link="'/carousel'">Carousel</a></li>
50 51 <li><a v-link="'/rate'">Rate</a></li>
51 52 </ul>
52 53 </nav>
... ...
test/main.js
... ... @@ -135,6 +135,11 @@ router.map({
135 135 require(['./routers/form.vue'], resolve);
136 136 }
137 137 },
  138 + '/carousel': {
  139 + component: function (resolve) {
  140 + require(['./routers/carousel.vue'], resolve);
  141 + }
  142 + },
138 143 '/rate': {
139 144 component: function (resolve) {
140 145 require(['./routers/rate.vue'], resolve);
... ...
test/routers/carousel.vue 0 → 100644
  1 +<template>
  2 + <Row>
  3 + <i-col span="2">
  4 + Current Index
  5 + <p>{{ currentIndex }}</p>
  6 + </i-col>
  7 + <i-col span="2">
  8 + <p>Autoplay</p>
  9 + <Switch :checked.sync="autoplay" size="small"></Switch>
  10 + </i-col>
  11 + <i-col span="4">
  12 + Speed <Slider :value.sync="autoplaySpeed" :min="300" :max="5000"></Slider>
  13 + </i-col>
  14 + <i-col span="4">
  15 + Switch To
  16 + <Button-group>
  17 + <i-button @click="currentIndex = 0">0</i-button>
  18 + <i-button @click="currentIndex = 1">1</i-button>
  19 + <i-button @click="currentIndex = 2">2</i-button>
  20 + </Button-group>
  21 + </i-col>
  22 + <i-col span="4">
  23 + <i-button @click="push">Push</i-button>
  24 + <i-button @click="remove = true">Remove Front</i-button>
  25 + </i-col>
  26 + </Row>
  27 + <Row>
  28 + <i-col span="4">
  29 + <p>Dots</p>
  30 + <Button-group>
  31 + <i-button @click="dots = 'inside'">Inside</i-button>
  32 + <i-button @click="dots = 'outside'">Outside</i-button>
  33 + <i-button @click="dots = 'none'">None</i-button>
  34 + </Button-group>
  35 + </i-col>
  36 + <i-col span="4">
  37 + <p>Trigger</p>
  38 + <Button-group>
  39 + <i-button @click="trigger = 'click'">Click</i-button>
  40 + <i-button @click="trigger = 'hover'">Hover</i-button>
  41 + </Button-group>
  42 + </i-col>
  43 + <i-col span="4">
  44 + Arrow
  45 + <Button-group>
  46 + <i-button @click="arrow = 'hover'">Hover</i-button>
  47 + <i-button @click="arrow = 'always'">Always</i-button>
  48 + <i-button @click="arrow = 'never'">Never</i-button>
  49 + </Button-group>
  50 + </i-col>
  51 + <i-col span="4">
  52 + Height
  53 + <i-button @click="height = 'auto'">Auto</i-button>
  54 + <i-button @click="height = 80">Manual</i-button>
  55 + <Slider v-if="height !== 'auto'" :value.sync="height" :min="50" :max="200"></Slider>
  56 + </i-col>
  57 + </Row>
  58 + <Carousel style="width: 50%; border: solid 1px #000; margin-top: 20px;"
  59 + :current-index.sync="currentIndex"
  60 + :autoplay="autoplay"
  61 + :autoplay-speed="autoplaySpeed"
  62 + :dots="dots"
  63 + :trigger="trigger"
  64 + :arrow="arrow"
  65 + :height="height"
  66 + @on-change="slideChange"
  67 + easing="linear">
  68 + <Carousel-item v-if="!remove">
  69 + <Alert type="warning" show-icon>
  70 + 警告提示文案
  71 + <template slot="desc">
  72 + 警告的提示描述文案警告的提示描述文案警告的提示描述文案
  73 + </template>
  74 + </Alert>
  75 + </Carousel-item>
  76 + <Carousel-item>
  77 + <div style="height: 150px; background: #f50; position: relative">
  78 + <p style="position: absolute; width: 100%; color: #fff; top: 50%; height: 20px; line-height: 20px; margin-top: -10px; text-align: center">test font 定高测试</p>
  79 + </div>
  80 + </Carousel-item>
  81 + <Carousel-item>
  82 + <div style="height: 100%; min-height: 20px; background: #f50; position: relative;">
  83 + <p style="position: absolute; width: 100%; color: #fff; top: 50%; height: 20px; line-height: 20px; margin-top: -10px; text-align: center">test font 居中测试</p>
  84 + </div>
  85 + </Carousel-item>
  86 + <Carousel-item>
  87 + <Card style="width:350px">
  88 + <p slot="title">
  89 + <Icon type="ios-film-outline"></Icon>
  90 + 经典电影
  91 + </p>
  92 + <a href="#" slot="extra">
  93 + <Icon type="ios-loop-strong"></Icon>
  94 + 换一换
  95 + </a>
  96 + <ul>
  97 +
  98 + </ul>
  99 + </Card>
  100 + </Carousel-item>
  101 + <Carousel-item style="text-align: center">
  102 + <Icon type="checkmark" style="font-size: 5em"></Icon>
  103 + </Carousel-item>
  104 + <Carousel-item>test3</Carousel-item>
  105 + <Carousel-item v-for="item in pushItem" track-by="$index">
  106 + <Icon type="checkmark" style="font-size: 5em"></Icon>{{item}}
  107 + </Carousel-item>
  108 + </Carousel>
  109 + <div style="max-height: 200px; overflow: scroll">
  110 + <p v-for="item in log" track-by="$index">{{item}}</p>
  111 + </div>
  112 +
  113 + <Card style="width:350px">
  114 + <p slot="title">
  115 + <Icon type="ios-film-outline"></Icon>
  116 + 经典电影
  117 + </p>
  118 + <Carousel>
  119 + <Carousel-item v-if="!remove">
  120 + <Alert type="warning" show-icon>
  121 + 警告提示文案
  122 + <template slot="desc">
  123 + 警告的提示描述文案警告的提示描述文案警告的提示描述文案
  124 + </template>
  125 + </Alert>
  126 + </Carousel-item>
  127 + <Carousel-item>
  128 + <div style="height: 100%; min-height: 20px; background: #f50; position: relative;">
  129 + <p style="position: absolute; width: 100%; color: #fff; top: 50%; height: 20px; line-height: 20px; margin-top: -10px; text-align: center">test font 定高测试</p>
  130 + </div>
  131 + </Carousel-item>
  132 + <Carousel-item style="text-align: center">
  133 + <Icon type="checkmark" style="font-size: 5em"></Icon>
  134 + </Carousel-item>
  135 + </Carousel>
  136 + </Card>
  137 + <Tabs>
  138 + <Tab-pane label="标签一">
  139 + <Carousel>
  140 + <Carousel-item v-if="!remove">
  141 + <Alert type="warning" show-icon>
  142 + 警告提示文案
  143 + <template slot="desc">
  144 + 警告的提示描述文案警告的提示描述文案警告的提示描述文案
  145 + </template>
  146 + </Alert>
  147 + </Carousel-item>
  148 + <Carousel-item>
  149 + <div style="height: 100%; min-height: 20px; background: #f50; position: relative;">
  150 + <p style="position: absolute; width: 100%; color: #fff; top: 50%; height: 20px; line-height: 20px; margin-top: -10px; text-align: center">test font 定高测试</p>
  151 + </div>
  152 + </Carousel-item>
  153 + <Carousel-item style="text-align: center">
  154 + <Icon type="checkmark" style="font-size: 5em"></Icon>
  155 + </Carousel-item>
  156 + </Carousel>
  157 + </Tab-pane>
  158 + <Tab-pane label="标签二" disabled>标签二的内容</Tab-pane>
  159 + <Tab-pane label="标签三">
  160 + <Carousel>
  161 + <Carousel-item v-if="!remove">
  162 + <Alert type="warning" show-icon>
  163 + 警告提示文案
  164 + <template slot="desc">
  165 + 警告的提示描述文案警告的提示描述文案警告的提示描述文案
  166 + </template>
  167 + </Alert>
  168 + </Carousel-item>
  169 + <Carousel-item>
  170 + <div style="height: 100%; min-height: 20px; background: #f50; position: relative;">
  171 + <p style="position: absolute; width: 100%; color: #fff; top: 50%; height: 20px; line-height: 20px; margin-top: -10px; text-align: center">test font 定高测试</p>
  172 + </div>
  173 + </Carousel-item>
  174 + <Carousel-item style="text-align: center">
  175 + <Icon type="checkmark" style="font-size: 5em"></Icon>
  176 + </Carousel-item>
  177 + </Carousel>
  178 + </Tab-pane>
  179 + </Tabs>
  180 +</template>
  181 +<script>
  182 + export default {
  183 + data () {
  184 + return {
  185 + currentIndex: 0,
  186 + autoplay: true,
  187 + autoplaySpeed: 2000,
  188 + remove: false,
  189 + pushItem: [],
  190 + arrow: 'hover',
  191 + trigger: 'click',
  192 + dots: 'inside',
  193 + height: 'auto',
  194 + log: []
  195 + }
  196 + },
  197 + methods: {
  198 + push () {
  199 + this.pushItem.push('test');
  200 + },
  201 + slideChange (from, to) {
  202 + this.log.push(`From ${from} To ${to}`);
  203 + }
  204 + }
  205 + }
  206 +</script>
... ...