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`; } 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({ export default defineComponent({
name, name,
@ -47,6 +49,10 @@ export default defineComponent({
type: [Number, String], type: [Number, String],
default: 40, default: 40,
}, },
startPosition: {
type: String as PropType<CircleStartPosition>,
default: 'top',
},
}, },
emits: ['update:currentRate'], emits: ['update:currentRate'],
@ -58,6 +64,22 @@ export default defineComponent({
const path = computed(() => getPath(props.clockwise, viewBoxSize.value)); 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( watch(
() => props.rate, () => props.rate,
(rate) => { (rate) => {
@ -160,7 +182,10 @@ export default defineComponent({
return () => ( return () => (
<div class={bem()} style={getSizeStyle(props.size)}> <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()} {renderGradient()}
{renderLayer()} {renderLayer()}
{renderHover()} {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 ## API
### Props ### Props
@ -136,6 +159,7 @@ export default {
| stroke-width | Stroke width | _number \| string_ | `40` | | stroke-width | Stroke width | _number \| string_ | `40` |
| stroke-linecap | Stroke linecapcan be set to `square` `butt` | _string_ | `round` | | stroke-linecap | Stroke linecapcan be set to `square` `butt` | _string_ | `round` |
| clockwise | Whether to be clockwise | _boolean_ | `true` | | clockwise | Whether to be clockwise | _boolean_ | `true` |
| start-position `v3.2.1` | Progress start positioncan be set to `left``right``bottom` | _CircleStartPosition_ | `top` |
### Slots ### 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 ## API
### Props ### Props
@ -148,6 +173,7 @@ export default {
| stroke-width | 进度条宽度 | _number \| string_ | `40` | | stroke-width | 进度条宽度 | _number \| string_ | `40` |
| stroke-linecap | 进度条端点的形状,可选值为 `square` `butt` | _string_ | `round` | | stroke-linecap | 进度条端点的形状,可选值为 `square` `butt` | _string_ | `round` |
| clockwise | 是否顺时针增加 | _boolean_ | `true` | | clockwise | 是否顺时针增加 | _boolean_ | `true` |
| start-position `v3.2.1` | 进度起始位置,可选值为 `left``right``bottom` | _CircleStartPosition_ | `top` |
### Slots ### Slots

View File

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

View File

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

View File

@ -152,4 +152,63 @@ exports[`should render demo and match snapshot 1`] = `
</div> </div>
</button> </button>
</div> </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(); 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)');
});