添加形状渐变功能
This commit is contained in:
parent
81f48438b1
commit
99171297df
|
@ -72,6 +72,11 @@ export interface PPTImageElement {
|
||||||
shadow?: PPTElementShadow;
|
shadow?: PPTElementShadow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ShapeGradient {
|
||||||
|
type: 'line' | 'radial';
|
||||||
|
color: [string, string];
|
||||||
|
rotate: number;
|
||||||
|
}
|
||||||
export interface PPTShapeElement {
|
export interface PPTShapeElement {
|
||||||
type: 'shape';
|
type: 'shape';
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -85,6 +90,7 @@ export interface PPTShapeElement {
|
||||||
path: string;
|
path: string;
|
||||||
fixedRatio: boolean;
|
fixedRatio: boolean;
|
||||||
fill: string;
|
fill: string;
|
||||||
|
gradient?: ShapeGradient;
|
||||||
rotate?: number;
|
rotate?: number;
|
||||||
outline?: PPTElementOutline;
|
outline?: PPTElementOutline;
|
||||||
opacity?: number;
|
opacity?: number;
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="flex: 2;">主题配色:</div>
|
<div style="flex: 2;">主题配色:</div>
|
||||||
<Popover trigger="click" v-model:visible="themePoolVisible">
|
<Popover trigger="click" placement="bottom" v-model:visible="themePoolVisible">
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="theme-pool">
|
<div class="theme-pool">
|
||||||
<div
|
<div
|
||||||
|
@ -257,11 +257,12 @@ export default defineComponent({
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 5px 20px;
|
margin: 0 -12px;
|
||||||
|
padding: 5px 32px;
|
||||||
transition: background-color .1s;
|
transition: background-color .1s;
|
||||||
|
|
||||||
& + .theme-item {
|
& + .theme-item {
|
||||||
margin-top: 8px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|
|
@ -1,18 +1,73 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="shape-style-panel">
|
<div class="shape-style-panel">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="flex: 2;">填充颜色:</div>
|
<Select
|
||||||
<Popover trigger="click">
|
style="flex: 10;"
|
||||||
|
:value="fillType"
|
||||||
|
@change="value => updateFillType(value)"
|
||||||
|
>
|
||||||
|
<SelectOption value="fill">纯色填充</SelectOption>
|
||||||
|
<SelectOption value="gradient">渐变填充</SelectOption>
|
||||||
|
</Select>
|
||||||
|
<div style="flex: 1;"></div>
|
||||||
|
<Popover trigger="click" v-if="fillType === 'fill'">
|
||||||
<template #content>
|
<template #content>
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
:modelValue="fill"
|
:modelValue="fill"
|
||||||
@update:modelValue="value => updateFill(value)"
|
@update:modelValue="value => updateFill(value)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<ColorButton :color="fill" style="flex: 3;" />
|
<ColorButton :color="fill" style="flex: 10;" />
|
||||||
</Popover>
|
</Popover>
|
||||||
|
<Select
|
||||||
|
style="flex: 10;"
|
||||||
|
:value="gradient.type"
|
||||||
|
@change="value => updateGradient({ type: value })"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<SelectOption value="line">线性渐变</SelectOption>
|
||||||
|
<SelectOption value="radial">径向渐变</SelectOption>
|
||||||
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<template v-if="fillType === 'gradient'">
|
||||||
|
<div class="row">
|
||||||
|
<div style="flex: 2;">起点颜色:</div>
|
||||||
|
<Popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<ColorPicker
|
||||||
|
:modelValue="gradient.color[0]"
|
||||||
|
@update:modelValue="value => updateGradient({ color: [value, gradient.color[1]] })"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<ColorButton :color="gradient.color[0]" style="flex: 3;" />
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div style="flex: 2;">终点颜色:</div>
|
||||||
|
<Popover trigger="click">
|
||||||
|
<template #content>
|
||||||
|
<ColorPicker
|
||||||
|
:modelValue="gradient.color[1]"
|
||||||
|
@update:modelValue="value => updateGradient({ color: [gradient.color[0], value] })"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<ColorButton :color="gradient.color[1]" style="flex: 3;" />
|
||||||
|
</Popover>
|
||||||
|
</div>
|
||||||
|
<div class="row" v-if="gradient.type === 'line'">
|
||||||
|
<div style="flex: 2;">渐变角度:</div>
|
||||||
|
<Slider
|
||||||
|
:min="0"
|
||||||
|
:max="360"
|
||||||
|
:step="15"
|
||||||
|
:value="gradient.rotate"
|
||||||
|
style="flex: 3;"
|
||||||
|
@change="value => updateGradient({ rotate: value })"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
<ElementOutline />
|
<ElementOutline />
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -26,7 +81,7 @@
|
||||||
import { computed, defineComponent, ref, Ref, watch } from 'vue'
|
import { computed, defineComponent, ref, Ref, watch } from 'vue'
|
||||||
import { useStore } from 'vuex'
|
import { useStore } from 'vuex'
|
||||||
import { MutationTypes, State } from '@/store'
|
import { MutationTypes, State } from '@/store'
|
||||||
import { PPTShapeElement } from '@/types/slides'
|
import { PPTShapeElement, ShapeGradient } from '@/types/slides'
|
||||||
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
import useHistorySnapshot from '@/hooks/useHistorySnapshot'
|
||||||
|
|
||||||
import ElementOpacity from '../common/ElementOpacity.vue'
|
import ElementOpacity from '../common/ElementOpacity.vue'
|
||||||
|
@ -47,14 +102,40 @@ export default defineComponent({
|
||||||
const handleElement: Ref<PPTShapeElement> = computed(() => store.getters.handleElement)
|
const handleElement: Ref<PPTShapeElement> = computed(() => store.getters.handleElement)
|
||||||
|
|
||||||
const fill = ref<string>()
|
const fill = ref<string>()
|
||||||
|
const gradient = ref<ShapeGradient>()
|
||||||
|
const fillType = ref('fill')
|
||||||
|
|
||||||
watch(handleElement, () => {
|
watch(handleElement, () => {
|
||||||
if(!handleElement.value) return
|
if(!handleElement.value) return
|
||||||
fill.value = handleElement.value.fill || '#000'
|
fill.value = handleElement.value.fill || '#000'
|
||||||
|
|
||||||
|
gradient.value = handleElement.value.gradient || { type: 'line', rotate: 0, color: [fill.value, '#fff'] }
|
||||||
|
|
||||||
|
fillType.value = handleElement.value.gradient ? 'gradient' : 'fill'
|
||||||
}, { deep: true, immediate: true })
|
}, { deep: true, immediate: true })
|
||||||
|
|
||||||
const { addHistorySnapshot } = useHistorySnapshot()
|
const { addHistorySnapshot } = useHistorySnapshot()
|
||||||
|
|
||||||
|
const updateFillType = (type: 'gradient' | 'fill') => {
|
||||||
|
if(type === 'fill') {
|
||||||
|
store.commit(MutationTypes.REMOVE_ELEMENT_PROPS, {
|
||||||
|
id: handleElement.value.id,
|
||||||
|
propName: 'gradient',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const props = { gradient: gradient.value }
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||||
|
}
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateGradient = (gradientProps: Partial<ShapeGradient>) => {
|
||||||
|
const props = { gradient: { ...gradient.value, ...gradientProps } }
|
||||||
|
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||||
|
addHistorySnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
const updateFill = (value: string) => {
|
const updateFill = (value: string) => {
|
||||||
const props = { fill: value }
|
const props = { fill: value }
|
||||||
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
store.commit(MutationTypes.UPDATE_ELEMENT, { id: handleElement.value.id, props })
|
||||||
|
@ -63,7 +144,11 @@ export default defineComponent({
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fill,
|
fill,
|
||||||
|
gradient,
|
||||||
|
fillType,
|
||||||
|
updateFillType,
|
||||||
updateFill,
|
updateFill,
|
||||||
|
updateGradient,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,6 +20,15 @@
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
>
|
>
|
||||||
|
<defs v-if="elementInfo.gradient">
|
||||||
|
<GradientDefs
|
||||||
|
:id="`base-gradient-${elementInfo.id}`"
|
||||||
|
:type="elementInfo.gradient.type"
|
||||||
|
:color1="elementInfo.gradient.color[0]"
|
||||||
|
:color2="elementInfo.gradient.color[1]"
|
||||||
|
:rotate="elementInfo.gradient.rotate"
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
<g
|
<g
|
||||||
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
||||||
>
|
>
|
||||||
|
@ -29,7 +38,7 @@
|
||||||
stroke-miterlimit="8"
|
stroke-miterlimit="8"
|
||||||
stroke-linejoin=""
|
stroke-linejoin=""
|
||||||
:d="elementInfo.path"
|
:d="elementInfo.path"
|
||||||
:fill="elementInfo.fill"
|
:fill="elementInfo.gradient ? `url(#base-gradient-${elementInfo.id})` : elementInfo.fill"
|
||||||
:stroke="outlineColor"
|
:stroke="outlineColor"
|
||||||
:stroke-width="outlineWidth"
|
:stroke-width="outlineWidth"
|
||||||
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
||||||
|
@ -46,8 +55,13 @@ import { PPTShapeElement } from '@/types/slides'
|
||||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
|
||||||
|
import GradientDefs from './GradientDefs.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'base-element-shape',
|
name: 'base-element-shape',
|
||||||
|
components: {
|
||||||
|
GradientDefs,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
type: Object as PropType<PPTShapeElement>,
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<linearGradient
|
||||||
|
v-if="type === 'line'"
|
||||||
|
:id="id"
|
||||||
|
x1="0%"
|
||||||
|
y1="0%"
|
||||||
|
x2="100%"
|
||||||
|
y2="0%"
|
||||||
|
:gradientTransform="`rotate(${rotate},0.5,0.5)`"
|
||||||
|
>
|
||||||
|
<stop offset="0%" :stop-color="color1" />
|
||||||
|
<stop offset="100%" :stop-color="color2" />
|
||||||
|
</linearGradient>
|
||||||
|
|
||||||
|
<radialGradient :id="id" v-else>
|
||||||
|
<stop offset="0%" :stop-color="color1" />
|
||||||
|
<stop offset="100%" :stop-color="color2" />
|
||||||
|
</radialGradient>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'gradient-defs',
|
||||||
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String as PropType<'line' | 'radial'>,
|
||||||
|
},
|
||||||
|
color1: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
color2: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
rotate: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -23,6 +23,15 @@
|
||||||
:width="elementInfo.width"
|
:width="elementInfo.width"
|
||||||
:height="elementInfo.height"
|
:height="elementInfo.height"
|
||||||
>
|
>
|
||||||
|
<defs v-if="elementInfo.gradient">
|
||||||
|
<GradientDefs
|
||||||
|
:id="`editabel-gradient-${elementInfo.id}`"
|
||||||
|
:type="elementInfo.gradient.type"
|
||||||
|
:color1="elementInfo.gradient.color[0]"
|
||||||
|
:color2="elementInfo.gradient.color[1]"
|
||||||
|
:rotate="elementInfo.gradient.rotate"
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
<g
|
<g
|
||||||
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
:transform="`scale(${elementInfo.width / elementInfo.viewBox}, ${elementInfo.height / elementInfo.viewBox}) translate(0,0) matrix(1,0,0,1,0,0)`"
|
||||||
>
|
>
|
||||||
|
@ -32,7 +41,7 @@
|
||||||
stroke-miterlimit="8"
|
stroke-miterlimit="8"
|
||||||
stroke-linejoin=""
|
stroke-linejoin=""
|
||||||
:d="elementInfo.path"
|
:d="elementInfo.path"
|
||||||
:fill="elementInfo.fill"
|
:fill="elementInfo.gradient ? `url(#editabel-gradient-${elementInfo.id})` : elementInfo.fill"
|
||||||
:stroke="outlineColor"
|
:stroke="outlineColor"
|
||||||
:stroke-width="outlineWidth"
|
:stroke-width="outlineWidth"
|
||||||
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
:stroke-dasharray="outlineStyle === 'dashed' ? '10 5' : '0 0'"
|
||||||
|
@ -50,8 +59,13 @@ import { ContextmenuItem } from '@/components/Contextmenu/types'
|
||||||
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
import useElementOutline from '@/views/components/element/hooks/useElementOutline'
|
||||||
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
import useElementShadow from '@/views/components/element/hooks/useElementShadow'
|
||||||
|
|
||||||
|
import GradientDefs from './GradientDefs.vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'editable-element-shape',
|
name: 'editable-element-shape',
|
||||||
|
components: {
|
||||||
|
GradientDefs,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
elementInfo: {
|
elementInfo: {
|
||||||
type: Object as PropType<PPTShapeElement>,
|
type: Object as PropType<PPTShapeElement>,
|
||||||
|
|
Loading…
Reference in New Issue