Skip to content

Commit

Permalink
Merge pull request #11479 from toddWang23/feat-carousel-vertical
Browse files Browse the repository at this point in the history
feat: 为carousel增加纵向模式和鼠标滑动模式
  • Loading branch information
allenve authored Jan 9, 2025
2 parents b355fda + 4a06f87 commit 495b056
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 3 deletions.
66 changes: 66 additions & 0 deletions packages/amis-ui/scss/components/_carousel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,70 @@
z-index: 100;
text-align: center;
}

&-vertical.#{$ns}Carousel {
.#{$ns}Carousel {
&-dotsControl {
right: 0;
bottom: auto;
height: 100%;
width: auto;
display: flex;
flex-direction: column;
top: 0;
justify-content: center;
}

&-leftArrow, &-rightArrow {
height: 10%;
width: 100%;
left: 0;
right: 0;
bottom: unset;
}

&-leftArrow {
top: 0;
svg {
transform: translate(-50%, -50%) rotate(90deg);
}
}

&-rightArrow {
bottom: 0;
top: unset;
svg {
transform: translate(-50%, -50%) rotate(90deg);
}
}

&-item.slide {
transform: translateY(100%);
}

&-item.slide.in {
transform: translateY(0);
}

&-item.slide.out {
transform: translateY(-100%);
}

&-item.slideRight {
transform: translateY(-100%);
}

&-item.slideRight.in {
transform: translateY(0);
}

&-item.slideRight.out {
transform: translateY(100%);
}
}
}

img {
pointer-events: none;
}
}
115 changes: 112 additions & 3 deletions packages/amis/src/renderers/Carousel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {MouseEvent, TouchEvent} from 'react';
import Transition, {
ENTERED,
ENTERING,
Expand Down Expand Up @@ -32,6 +32,22 @@ export interface CarouselSchema extends BaseSchema {
*/
type: 'carousel';

/**
* 轮播图方向,默认为水平方向
*/
direction?: 'horizontal' | 'vertical';

/**
* 是否循环播放, 默认为 true。
*/
loop?: boolean;

/**
* 是否支持鼠标事件
* 默认为 true。
*/
mouseEvent?: boolean;

/**
* 是否自动播放
*/
Expand Down Expand Up @@ -130,6 +146,7 @@ export interface CarouselState {
current: number;
options: any[];
nextAnimation: string;
mouseStartLocation: number | null;
}

const defaultSchema = {
Expand Down Expand Up @@ -164,6 +181,8 @@ const defaultSchema = {
}
};

const SCROLL_THRESHOLD = 20;

export class Carousel extends React.Component<CarouselProps, CarouselState> {
wrapperRef: React.RefObject<HTMLDivElement> = React.createRef();
intervalTimeout: NodeJS.Timer | number;
Expand Down Expand Up @@ -195,7 +214,8 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
state = {
current: 0,
options: this.props.options || getPropValue(this.props) || [],
nextAnimation: ''
nextAnimation: '',
mouseStartLocation: null
};

loading: boolean = false;
Expand Down Expand Up @@ -284,6 +304,16 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
async transitFramesTowards(direction: string, nextAnimation: string) {
let {current} = this.state;
let prevIndex = current;

// 如果这里是不循环状态,需要阻止切换到第一张或者最后一张图片
if (
this.props.loop === false &&
((current === 0 && direction === 'right') ||
(current === this.state.options.length - 1 && direction === 'left'))
) {
return;
}

switch (direction) {
case 'left':
current = this.getFrameId('next');
Expand Down Expand Up @@ -445,6 +475,73 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
return newOptions;
}

/**
* 获取事件发生的屏幕坐标,兼容鼠标事件和触摸事件。
*
* @param event 事件对象,可以是鼠标事件或触摸事件
* @returns 返回包含屏幕横纵坐标的对象
*/
getEventScreenXY(
event: MouseEvent<HTMLDivElement> | TouchEvent<HTMLDivElement>
) {
let screenX, screenY;
if ((event as MouseEvent<HTMLDivElement>).screenX !== undefined) {
screenX = (event as MouseEvent<HTMLDivElement>).screenX;
screenY = (event as MouseEvent<HTMLDivElement>).screenY;
} else {
screenX = (event as TouchEvent<HTMLDivElement>).touches[0].screenX;
screenY = (event as TouchEvent<HTMLDivElement>).touches[0].screenY;
}

return {
screenX,
screenY
};
}

/**
* 添加鼠标按下事件监听器, 用于判断滑动方向
* @param event 鼠标事件对象
*/
@autobind
addMouseDownListener(
event: MouseEvent<HTMLDivElement> | TouchEvent<HTMLDivElement>
) {
const {direction} = this.props;

const {screenX, screenY} = this.getEventScreenXY(event);

this.setState({
mouseStartLocation: direction === 'vertical' ? screenY : screenX // 根据当前滑动方向确定是应该使用x坐标还是y坐标做mark
});
}

/**
* 添加鼠标抬起事件监听器, 用于判断滑动方向
* @param event 鼠标事件对象
*/
@autobind
addMouseUpListener(
event: MouseEvent<HTMLDivElement> | TouchEvent<HTMLDivElement>
) {
const {screenX, screenY} = this.getEventScreenXY(event);

// 根据当前滑动方向确定是应该使用x坐标还是y坐标做mark
const {direction} = this.props;
const location = direction === 'vertical' ? screenY : screenX;

if (this.state.mouseStartLocation !== null) {
if (location - this.state.mouseStartLocation > SCROLL_THRESHOLD) {
this.autoSlide('prev');
} else if (this.state.mouseStartLocation - location > SCROLL_THRESHOLD) {
this.autoSlide();
}
this.setState({
mouseStartLocation: null
});
}
}

render() {
const {
render,
Expand Down Expand Up @@ -630,8 +727,20 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
name: 'wrapperCustomStyle',
id,
themeCss: wrapperCustomStyle
})
}),
{'Carousel-vertical': this.props.direction === 'vertical'}
)}
onMouseDown={
this.props.mouseEvent ? this.addMouseDownListener : undefined
}
onMouseUp={this.props.mouseEvent ? this.addMouseUpListener : undefined}
onMouseLeave={
this.props.mouseEvent ? this.addMouseUpListener : undefined
}
onTouchStart={
this.props.mouseEvent ? this.addMouseDownListener : undefined
}
onTouchEnd={this.props.mouseEvent ? this.addMouseUpListener : undefined}
style={carouselStyles}
>
{body ? body : placeholder}
Expand Down

0 comments on commit 495b056

Please sign in to comment.