Skip to content

Commit dd2aebd

Browse files
authored
feat(ol-style-fill): add fill color gradient (#375)
docs were added to the polygon geometry demo
1 parent 64fd40d commit dd2aebd

File tree

4 files changed

+353
-63
lines changed

4 files changed

+353
-63
lines changed

Diff for: docs/componentsguide/styles/fill/index.md

+33
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,36 @@ Styling a feature
4040
- **Type**: `Number`
4141

4242
The color either in hexadecimal or as RGB array with red, green, and blue values between 0 and 255 and alpha between 0 and 1 inclusive.
43+
44+
### gradient
45+
- **Type**: `Object`
46+
```
47+
{
48+
"type": "linear", // Type of gradient. Here, it's a linear gradient.
49+
"x0": 0, // x-coordinate of the starting point. Indicates the horizontal position where the gradient starts.
50+
"y0": 0, // y-coordinate of the starting point. Indicates the vertical position where the gradient starts.
51+
"x1": 0, // x-coordinate of the ending point. This is the horizontal position where the gradient ends. It’s the same as x0, meaning the gradient is vertical.
52+
"y1": 256, // y-coordinate of the ending point. Indicates the vertical position where the gradient ends, extending the gradient to y = 256.
53+
"colorStops": [
54+
[0, "red"], // Start color at position 0. The color at the beginning of the gradient is red.
55+
[0.5, "yellow"], // Middle color at position 0.5. The color at the midpoint of the gradient (y = 128) is yellow.
56+
[1, "green"] // End color at position 1. The color at the end of the gradient (y = 256) is green.
57+
]
58+
}
59+
60+
{
61+
"type": "radial", // Type of gradient. Here, it's a radial gradient.
62+
"x0": 128, // x-coordinate of the starting circle's center. Specifies the horizontal position of the starting circle.
63+
"y0": 128, // y-coordinate of the starting circle's center. Specifies the vertical position of the starting circle.
64+
"r0": 0, // Radius of the starting circle. Here, it's 0, meaning the gradient starts at a single point.
65+
"x1": 128, // x-coordinate of the ending circle's center. Same as x0, so the ending circle is centered at the same horizontal position.
66+
"y1": 128, // y-coordinate of the ending circle's center. Same as y0, so the ending circle is centered at the same vertical position.
67+
"r1": 128, // Radius of the ending circle. Specifies the radius of the circle where the gradient ends, extending to a radius of 128 units.
68+
"colorStops": [
69+
[0, "blue"], // Color at the center of the gradient (r = 0). The color at the very center of the radial gradient is blue.
70+
[0.5, "cyan"], // Color at the midpoint of the gradient. At the midpoint of the radius (r = 64), the color is cyan.
71+
[1, "white"] // Color at the edge of the gradient (r = 128). The color at the outer edge of the radial gradient is white.
72+
]
73+
}
74+
75+
```

Diff for: src/components/styles/OlStyleFill.vue

+163-44
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,143 @@
11
<template>
22
<div v-if="false"></div>
33
</template>
4+
45
<script setup lang="ts">
6+
import { type Ref, inject, watch, onMounted, onUnmounted, ref } from "vue";
57
import Fill from "ol/style/Fill";
68
import type CircleStyle from "ol/style/Circle";
7-
8-
import type { Ref } from "vue";
9-
import { inject, watch, onMounted, onUnmounted } from "vue";
109
import type Style from "ol/style/Style";
1110
import usePropsAsObjectProperties from "@/composables/usePropsAsObjectProperties";
1211
13-
const props = withDefaults(
14-
defineProps<{
15-
color?: string;
16-
}>(),
17-
{},
18-
);
12+
// Define the type for gradient color stops
13+
type GradientColorStop = [number, string];
14+
15+
// Define the type for linear gradients
16+
type LinearGradient = {
17+
type: "linear";
18+
x0: number;
19+
y0: number;
20+
x1: number;
21+
y1: number;
22+
colorStops: GradientColorStop[];
23+
};
24+
25+
// Define the type for radial gradients
26+
type RadialGradient = {
27+
type: "radial";
28+
x0: number;
29+
y0: number;
30+
r0: number;
31+
x1: number;
32+
y1: number;
33+
r1: number;
34+
colorStops: GradientColorStop[];
35+
};
36+
37+
// Define the type for conic gradients
38+
type ConicGradient = {
39+
type: "conic";
40+
cx: number;
41+
cy: number;
42+
radius: number;
43+
startAngle: number;
44+
endAngle: number;
45+
colorStops: GradientColorStop[];
46+
};
47+
48+
// Define a union type for all gradient types
49+
type Gradient = LinearGradient | RadialGradient | ConicGradient;
50+
51+
// Define the props type for the component
52+
const props = defineProps<{
53+
color?: string;
54+
gradient?: Gradient;
55+
}>();
1956
20-
const style = inject<Ref<Style | null> | null>("style", null);
21-
const circle = inject<Ref<CircleStyle | null> | null>("circle", null);
22-
const styledObj = inject<Ref<Style | null> | null>("styledObj", null);
57+
// Inject possible nullable Ref objects from the parent component
58+
const style = inject<Ref<Style | null>>("style", ref(null));
59+
const circle = inject<Ref<CircleStyle | null>>("circle", ref(null));
60+
const styledObj = inject<Ref<Style | null>>("styledObj", ref(null));
2361
62+
// Use a custom composable to convert props into an object
2463
const properties = usePropsAsObjectProperties(props);
2564
26-
if (style != null && circle == null) {
27-
// in style object
28-
let fill = new Fill(properties);
29-
style?.value?.setFill(fill);
30-
31-
const applyFill = () => {
32-
style?.value?.setFill(new Fill());
33-
fill = new Fill(properties);
34-
style?.value?.setFill(fill);
35-
};
36-
watch(properties, () => {
37-
applyFill();
38-
});
65+
// Function to create a gradient fill
66+
const createGradientFill = (
67+
gradient?: Gradient,
68+
width: number = 256,
69+
height: number = 256,
70+
): Fill => {
71+
const gradientCanvas = document.createElement("canvas");
72+
const ctx = gradientCanvas.getContext("2d");
3973
40-
watch(style, () => {
41-
applyFill();
42-
});
74+
if (!ctx) throw new Error("Unable to get canvas context");
4375
44-
onMounted(() => {
45-
style?.value?.setFill(fill);
46-
});
76+
gradientCanvas.width = width;
77+
gradientCanvas.height = height;
4778
48-
onUnmounted(() => {
49-
style?.value?.setFill(new Fill());
50-
});
51-
} else if (circle != null) {
52-
// in circle
53-
const applyFilltoCircle = (color?: string) => {
79+
let grad: CanvasGradient;
80+
81+
// Create the corresponding CanvasGradient object based on the gradient type
82+
if (gradient) {
83+
switch (gradient.type) {
84+
case "linear":
85+
grad = ctx.createLinearGradient(
86+
gradient.x0,
87+
gradient.y0,
88+
gradient.x1,
89+
gradient.y1,
90+
);
91+
break;
92+
case "radial":
93+
grad = ctx.createRadialGradient(
94+
gradient.x0,
95+
gradient.y0,
96+
gradient.r0,
97+
gradient.x1,
98+
gradient.y1,
99+
gradient.r1,
100+
);
101+
break;
102+
case "conic":
103+
// Conic gradients are not directly supported by the Canvas API, use a linear gradient as a fallback
104+
grad = ctx.createLinearGradient(0, 0, width, height);
105+
break;
106+
default:
107+
throw new Error("Unsupported gradient type");
108+
}
109+
110+
// Add color stops to the gradient
111+
gradient.colorStops.forEach(([offset, gradientColor]) => {
112+
grad.addColorStop(offset, gradientColor);
113+
});
114+
115+
ctx.fillStyle = grad;
116+
} else {
117+
ctx.fillStyle = properties.color ?? "transparent";
118+
}
119+
120+
ctx.fillRect(0, 0, width, height);
121+
122+
const dataURL = gradientCanvas.toDataURL();
123+
return new Fill({ color: { src: dataURL } });
124+
};
125+
126+
// Function to apply fill style to a Style object
127+
const applyFillToStyle = () => {
128+
if (style.value) {
129+
const fill = properties.gradient
130+
? createGradientFill(properties.gradient)
131+
: new Fill({ color: properties.color || "transparent" });
132+
133+
style.value.setFill(fill);
134+
}
135+
};
136+
137+
// Function to apply fill style to a CircleStyle object
138+
const applyFillToCircle = (color?: string) => {
139+
if (circle.value) {
140+
// @ts-ignore
54141
circle?.value?.getFill().setColor(color || null);
55142
circle?.value?.setRadius(circle?.value.getRadius()); // force render
56143
try {
@@ -60,15 +147,47 @@ if (style != null && circle == null) {
60147
// @ts-ignore
61148
styledObj?.value.changed();
62149
}
63-
};
150+
}
151+
};
64152
65-
applyFilltoCircle(properties.color);
153+
// Conditional style handling
154+
if (style.value && !circle.value) {
155+
// If a Style object exists but not a CircleStyle object, apply style to the Style
156+
applyFillToStyle();
66157
67-
watch(properties, () => {
68-
applyFilltoCircle(properties.color);
69-
});
70-
watch(circle, () => {
71-
applyFilltoCircle(properties.color);
158+
// Watch for changes in properties and reapply style
159+
watch(properties, applyFillToStyle, { immediate: true });
160+
watch(
161+
style,
162+
(newStyle) => {
163+
if (newStyle) applyFillToStyle();
164+
},
165+
{ immediate: true },
166+
);
167+
168+
onMounted(applyFillToStyle);
169+
onUnmounted(() => {
170+
style.value?.setFill(new Fill());
72171
});
172+
} else if (circle.value) {
173+
// If a CircleStyle object exists, apply style to the CircleStyle
174+
applyFillToCircle(properties.color);
175+
176+
// Watch for changes in properties and the CircleStyle object and reapply style
177+
watch(
178+
properties,
179+
() => {
180+
if (circle.value) applyFillToCircle(properties.color);
181+
},
182+
{ immediate: true },
183+
);
184+
185+
watch(
186+
circle,
187+
(newCircle) => {
188+
if (newCircle) applyFillToCircle(properties.color);
189+
},
190+
{ immediate: true },
191+
);
73192
}
74193
</script>

Diff for: src/demos/MultiPoint.vue

+81-19
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,7 @@
1919
<ol-source-vector>
2020
<ol-feature>
2121
<ol-geom-multi-point
22-
:coordinates="[
23-
[116.544921, 40.451633],
24-
[116.545264, 40.451649],
25-
[116.545865, 40.451698],
26-
[116.546144, 40.451551],
27-
[116.546337, 40.451274],
28-
[116.546788, 40.451143],
29-
[116.547324, 40.451078],
30-
[116.547539, 40.450996],
31-
[116.547839, 40.450719],
32-
[116.54844, 40.450506],
33-
[116.548933, 40.450604],
34-
[116.549448, 40.450604],
35-
[116.550242, 40.450376],
36-
[116.550865, 40.450163],
37-
[116.551702, 40.449935],
38-
[116.552581, 40.449576],
39-
]"
22+
:coordinates="[[116.547539, 40.450996]]"
4023
></ol-geom-multi-point>
4124
<ol-style>
4225
<ol-style-circle :radius="radius">
@@ -48,6 +31,57 @@
4831
</ol-style-circle>
4932
</ol-style>
5033
</ol-feature>
34+
35+
<ol-feature>
36+
<ol-geom-polygon
37+
:coordinates="[
38+
[
39+
[116.54825, 40.44664],
40+
[116.55125, 40.44664],
41+
[116.55175, 40.45364],
42+
[116.54875, 40.45364],
43+
[116.54825, 40.44664],
44+
],
45+
]"
46+
></ol-geom-polygon>
47+
<ol-style>
48+
<ol-style-fill color="red" />
49+
</ol-style>
50+
</ol-feature>
51+
52+
<ol-feature>
53+
<ol-geom-polygon
54+
:coordinates="[
55+
[
56+
[116.55325, 40.44364],
57+
[116.55625, 40.44364],
58+
[116.55675, 40.45164],
59+
[116.55375, 40.45164],
60+
[116.55325, 40.44364],
61+
],
62+
]"
63+
></ol-geom-polygon>
64+
<ol-style>
65+
<ol-style-fill :gradient="linearGradient" />
66+
</ol-style>
67+
</ol-feature>
68+
69+
<ol-feature>
70+
<ol-geom-polygon
71+
:coordinates="[
72+
[
73+
[116.53415, 40.45154],
74+
[116.54245, 40.45154],
75+
[116.54385, 40.44664],
76+
[116.53555, 40.44664],
77+
[116.53415, 40.45154],
78+
],
79+
]"
80+
></ol-geom-polygon>
81+
<ol-style>
82+
<ol-style-fill :gradient="radialGradient" />
83+
</ol-style>
84+
</ol-feature>
5185
</ol-source-vector>
5286
</ol-vector-layer>
5387
</ol-map>
@@ -58,9 +92,37 @@ import { ref } from "vue";
5892
5993
const center = ref([116.54875, 40.45064]);
6094
const projection = ref("EPSG:4326");
61-
const zoom = ref(17);
95+
const zoom = ref(15);
6296
const radius = ref(10);
6397
const strokeWidth = ref(4);
6498
const strokeColor = ref("red");
6599
const fillColor = ref("white");
100+
101+
const linearGradient = {
102+
type: "linear",
103+
x0: 0,
104+
y0: 0,
105+
x1: 0,
106+
y1: 256,
107+
colorStops: [
108+
[0, "red"], // Start color
109+
[0.5, "yellow"], // Middle color
110+
[1, "green"], // End color
111+
],
112+
};
113+
114+
const radialGradient = {
115+
type: "radial",
116+
x0: 128,
117+
y0: 128,
118+
r0: 0,
119+
x1: 128,
120+
y1: 128,
121+
r1: 128,
122+
colorStops: [
123+
[0, "blue"], // Center color
124+
[0.5, "cyan"], // Middle color
125+
[1, "white"], // Edge color
126+
],
127+
};
66128
</script>

0 commit comments

Comments
 (0)