feat(Circle): add startPosition prop (#9305)

* feat(Circle): add startPosition prop

* docs(Circle): add start-position demo
This commit is contained in:
neverland 2021-08-22 16:19:37 +08:00 committed by GitHub
parent 5a93179b6b
commit f24c521d26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 184 additions and 1 deletions

View File

@ -17,6 +17,8 @@ function getPath(clockwise: boolean, viewBoxSize: number) {
} m 0, -500 a 500, 500 0 1, ${sweepFlag} 0, 1000 a 500, 500 0 1, ${sweepFlag} 0, -1000`;
}
export type CircleStartPosition = 'top' | 'right' | 'bottom' | 'left';
export default defineComponent({
name,
@ -47,6 +49,10 @@ export default defineComponent({
type: [Number, String],
default: 40,
},
startPosition: {
type: String as PropType<CircleStartPosition>,
default: 'top',
},
},
emits: ['update:currentRate'],
@ -58,6 +64,22 @@ export default defineComponent({
const path = computed(() => getPath(props.clockwise, viewBoxSize.value));
const svgStyle = computed(() => {
const ROTATE_ANGLE_MAP: Record<CircleStartPosition, number> = {
top: 0,
right: 90,
bottom: 180,
left: 270,
};
const angleValue = ROTATE_ANGLE_MAP[props.startPosition];
if (angleValue) {
return {
transform: `rotate(${angleValue}deg)`,
};
}
});
watch(
() => props.rate,
(rate) => {
@ -160,7 +182,10 @@ export default defineComponent({
return () => (
<div class={bem()} style={getSizeStyle(props.size)}>
<svg viewBox={`0 0 ${viewBoxSize.value} ${viewBoxSize.value}`}>
<svg
viewBox={`0 0 ${viewBoxSize.value} ${viewBoxSize.value}`}
style={svgStyle.value}
>
{renderGradient()}
{renderLayer()}
{renderHover()}

View File

@ -119,6 +119,29 @@ export default {
/>
```
### Start Position
```html
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
:text="Left"
start-position="left"
/>
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
text="Right"
start-position="right"
/>
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
text="Bottom"
start-position="bottom"
/>
```
## API
### Props
@ -136,6 +159,7 @@ export default {
| stroke-width | Stroke width | _number \| string_ | `40` |
| stroke-linecap | Stroke linecapcan be set to `square` `butt` | _string_ | `round` |
| clockwise | Whether to be clockwise | _boolean_ | `true` |
| start-position `v3.2.1` | Progress start positioncan be set to `left``right``bottom` | _CircleStartPosition_ | `top` |
### Slots

View File

@ -131,6 +131,31 @@ export default {
/>
```
### 起始位置
进度条默认从顶部开始,可以通过 `start-position` 属性设置起始位置。
```html
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
:text="左侧"
start-position="left"
/>
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
text="右侧"
start-position="right"
/>
<van-circle
v-model:current-rate="currentRate"
:rate="rate"
text="底部"
start-position="bottom"
/>
```
## API
### Props
@ -148,6 +173,7 @@ export default {
| stroke-width | 进度条宽度 | _number \| string_ | `40` |
| stroke-linecap | 进度条端点的形状,可选值为 `square` `butt` | _string_ | `round` |
| clockwise | 是否顺时针增加 | _boolean_ | `true` |
| start-position `v3.2.1` | 进度起始位置,可选值为 `left``right``bottom` | _CircleStartPosition_ | `top` |
### Slots

View File

@ -6,19 +6,27 @@ const format = (rate: number) => Math.min(Math.max(rate, 0), 100);
const i18n = {
'zh-CN': {
left: '左侧',
right: '右侧',
bottom: '底部',
gradient: '渐变色',
customSize: '大小定制',
customStyle: '样式定制',
customColor: '颜色定制',
customWidth: '宽度定制',
startPosition: '起始位置',
counterClockwise: '逆时针',
},
'en-US': {
left: 'Left',
right: 'Right',
bottom: 'Bottom',
gradient: 'Gradient',
customSize: 'Custom Size',
customStyle: 'Custom Style',
customColor: 'Custom Color',
customWidth: 'Custom Width',
startPosition: 'Start Position',
counterClockwise: 'Counter Clockwise',
},
};
@ -111,6 +119,27 @@ const reduce = () => {
@click="reduce"
/>
</div>
<demo-block :title="t('startPosition')">
<van-circle
:current-rate="75"
:rate="rate"
:text="t('left')"
start-position="left"
/>
<van-circle
:current-rate="75"
:rate="rate"
:text="t('right')"
start-position="right"
/>
<van-circle
:current-rate="75"
:rate="rate"
:text="t('bottom')"
start-position="bottom"
/>
</demo-block>
</template>
<style lang="less">

View File

@ -5,3 +5,4 @@ const Circle = withInstall<typeof _Circle>(_Circle);
export default Circle;
export { Circle };
export type { CircleStartPosition } from './Circle';

View File

@ -152,4 +152,63 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</button>
</div>
<div>
<div class="van-circle">
<svg viewbox="0 0 1040 1040"
style="transform: rotate(270deg);"
>
<path class="van-circle__layer"
style="fill: none; stroke-width: 40px;"
d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
>
</path>
<path d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
style="stroke-width: 41px; stroke-dasharray: 2355px 3140px;"
class="van-circle__hover"
>
</path>
</svg>
<div class="van-circle__text">
Left
</div>
</div>
<div class="van-circle">
<svg viewbox="0 0 1040 1040"
style="transform: rotate(90deg);"
>
<path class="van-circle__layer"
style="fill: none; stroke-width: 40px;"
d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
>
</path>
<path d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
style="stroke-width: 41px; stroke-dasharray: 2355px 3140px;"
class="van-circle__hover"
>
</path>
</svg>
<div class="van-circle__text">
Right
</div>
</div>
<div class="van-circle">
<svg viewbox="0 0 1040 1040"
style="transform: rotate(180deg);"
>
<path class="van-circle__layer"
style="fill: none; stroke-width: 40px;"
d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
>
</path>
<path d="M 520 520 m 0, -500 a 500, 500 0 1, 1 0, 1000 a 500, 500 0 1, 1 0, -1000"
style="stroke-width: 41px; stroke-dasharray: 2355px 3140px;"
class="van-circle__hover"
>
</path>
</svg>
<div class="van-circle__text">
Bottom
</div>
</div>
</div>
`;

View File

@ -47,3 +47,22 @@ test('should change stroke linecap when using stroke-linecap prop', () => {
expect(wrapper.html()).toMatchSnapshot();
});
test('should render start-position prop correctly', async () => {
const wrapper = mount(Circle, {
props: {
startPosition: 'top',
},
});
expect(wrapper.find('svg').style.transform).toEqual('');
await wrapper.setProps({ startPosition: 'right' });
expect(wrapper.find('svg').style.transform).toEqual('rotate(90deg)');
await wrapper.setProps({ startPosition: 'bottom' });
expect(wrapper.find('svg').style.transform).toEqual('rotate(180deg)');
await wrapper.setProps({ startPosition: 'left' });
expect(wrapper.find('svg').style.transform).toEqual('rotate(270deg)');
});