@@ -75,14 +75,16 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
75
75
76
76
@override
77
77
void drawRect (ui.Rect rect, SurfacePaintData paint) {
78
+ rect = adjustRectForDom (rect, paint);
78
79
currentElement.append (
79
80
buildDrawRectElement (rect, paint, 'draw-rect' , currentTransform));
80
81
}
81
82
82
83
@override
83
84
void drawRRect (ui.RRect rrect, SurfacePaintData paint) {
85
+ final ui.Rect outerRect = adjustRectForDom (rrect.outerRect, paint);
84
86
final DomElement element = buildDrawRectElement (
85
- rrect. outerRect, paint, 'draw-rrect' , currentTransform);
87
+ outerRect, paint, 'draw-rrect' , currentTransform);
86
88
applyRRectBorderRadius (element.style, rrect);
87
89
currentElement.append (element);
88
90
}
@@ -160,8 +162,77 @@ ui.Color blurColor(ui.Color color, double sigma) {
160
162
return ui.Color ((reducedAlpha & 0xff ) << 24 | (color.value & 0x00ffffff ));
161
163
}
162
164
165
+ /// When drawing a shape (rect, rrect, circle, etc) in DOM/CSS, the [rect] given
166
+ /// by Flutter needs to be adjusted to what DOM/CSS expect.
167
+ ///
168
+ /// This method takes Flutter's [rect] and produces a new rect that can be used
169
+ /// to generate the correct CSS properties to match Flutter's expectations.
170
+ ///
171
+ ///
172
+ /// Here's what Flutter's given [rect] and [paint.strokeWidth] represent:
173
+ ///
174
+ /// top-left ↓
175
+ /// ┌──↓──────────────────────┐
176
+ /// →→→→x x │←←
177
+ /// │ ┌───────────────┐ │ |
178
+ /// │ │ │ │ |
179
+ /// │ │ │ │ | height
180
+ /// │ │ │ │ |
181
+ /// │ └───────────────┘ │ |
182
+ /// │ x x │←←
183
+ /// └─────────────────────────┘
184
+ /// stroke-width ↑----↑ ↑
185
+ /// ↑-------------------↑ width
186
+ ///
187
+ ///
188
+ ///
189
+ /// In the DOM/CSS, here's how the coordinates should look like:
190
+ ///
191
+ /// top-left ↓
192
+ /// →→x─────────────────────────┐
193
+ /// │ │
194
+ /// │ x───────────────x │←←
195
+ /// │ │ │ │ |
196
+ /// │ │ │ │ | height
197
+ /// │ │ │ │ |
198
+ /// │ x───────────────x │←←
199
+ /// │ │
200
+ /// └─────────────────────────┘
201
+ /// border-width ↑----↑ ↑
202
+ /// ↑---------------↑ width
203
+ ///
204
+ /// As shown in the drawing above, the width/height don't start at the top-left
205
+ /// coordinates. Instead, they start from the inner top-left (inside the border).
206
+ ui.Rect adjustRectForDom (ui.Rect rect, SurfacePaintData paint) {
207
+ double left = math.min (rect.left, rect.right);
208
+ double top = math.min (rect.top, rect.bottom);
209
+ double width = rect.width.abs ();
210
+ double height = rect.height.abs ();
211
+
212
+ final bool isStroke = paint.style == ui.PaintingStyle .stroke;
213
+ final double strokeWidth = paint.strokeWidth ?? 0.0 ;
214
+ if (isStroke && strokeWidth > 0.0 ) {
215
+ left -= strokeWidth / 2.0 ;
216
+ top -= strokeWidth / 2.0 ;
217
+
218
+ // width and height shouldn't go below zero.
219
+ width = math.max (0 , width - strokeWidth);
220
+ height = math.max (0 , height - strokeWidth);
221
+ }
222
+
223
+ if (left != rect.left ||
224
+ top != rect.top ||
225
+ width != rect.width ||
226
+ height != rect.height) {
227
+ return ui.Rect .fromLTWH (left, top, width, height);
228
+ }
229
+ return rect;
230
+ }
231
+
163
232
DomHTMLElement buildDrawRectElement (
164
233
ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) {
234
+ assert (rect.left <= rect.right);
235
+ assert (rect.top <= rect.bottom);
165
236
final DomHTMLElement rectangle = domDocument.createElement (tagName) as
166
237
DomHTMLElement ;
167
238
assert (() {
@@ -172,26 +243,11 @@ DomHTMLElement buildDrawRectElement(
172
243
String effectiveTransform;
173
244
final bool isStroke = paint.style == ui.PaintingStyle .stroke;
174
245
final double strokeWidth = paint.strokeWidth ?? 0.0 ;
175
- final double left = math.min (rect.left, rect.right);
176
- final double right = math.max (rect.left, rect.right);
177
- final double top = math.min (rect.top, rect.bottom);
178
- final double bottom = math.max (rect.top, rect.bottom);
179
246
if (transform.isIdentity ()) {
180
- if (isStroke) {
181
- effectiveTransform =
182
- 'translate(${left - (strokeWidth / 2.0 )}px, ${top - (strokeWidth / 2.0 )}px)' ;
183
- } else {
184
- effectiveTransform = 'translate(${left }px, ${top }px)' ;
185
- }
247
+ effectiveTransform = 'translate(${rect .left }px, ${rect .top }px)' ;
186
248
} else {
187
- // Clone to avoid mutating _transform.
188
- final Matrix4 translated = transform.clone ();
189
- if (isStroke) {
190
- translated.translate (
191
- left - (strokeWidth / 2.0 ), top - (strokeWidth / 2.0 ));
192
- } else {
193
- translated.translate (left, top);
194
- }
249
+ // Clone to avoid mutating `transform`.
250
+ final Matrix4 translated = transform.clone ()..translate (rect.left, rect.top);
195
251
effectiveTransform = matrix4ToCssTransform (translated);
196
252
}
197
253
final DomCSSStyleDeclaration style = rectangle.style;
@@ -216,15 +272,14 @@ DomHTMLElement buildDrawRectElement(
216
272
}
217
273
}
218
274
275
+ style
276
+ ..width = '${rect .width }px'
277
+ ..height = '${rect .height }px' ;
278
+
219
279
if (isStroke) {
220
- style
221
- ..width = '${right - left - strokeWidth }px'
222
- ..height = '${bottom - top - strokeWidth }px'
223
- ..border = '${_borderStrokeToCssUnit (strokeWidth )} solid $cssColor ' ;
280
+ style.border = '${_borderStrokeToCssUnit (strokeWidth )} solid $cssColor ' ;
224
281
} else {
225
282
style
226
- ..width = '${right - left }px'
227
- ..height = '${bottom - top }px'
228
283
..backgroundColor = cssColor
229
284
..backgroundImage = _getBackgroundImageCssValue (paint.shader, rect);
230
285
}
0 commit comments