зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1501226 - Support for CSS transforms in the Flexbox highlighter r=gl
In many places we were using bounds to calculate an objects dimensions but that obviously doesn't work when an object is rotated e.g. {F588303} Also, we were using `getBoxQuads()`, which gives the co-ordinates of the translated object. We were then applying the transform matrix to the canvas even though the coordinates came from the object **after** it was already transformed. Anyhow, now we get the dimensions of objects as if they are not transformed and then apply the transformation matrix, which gives a great result every time. Differential Revision: https://phabricator.services.mozilla.com/D9805 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1b50b597e6
Коммит
af575ff9cd
|
@ -164,10 +164,11 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
// Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
|
// Clear the pattern cache to avoid dead object exceptions (Bug 1342051).
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
|
|
||||||
|
this.axes = null;
|
||||||
|
this.crossAxisDirection = null;
|
||||||
this.flexData = null;
|
this.flexData = null;
|
||||||
this.mainAxisDirection = null;
|
this.mainAxisDirection = null;
|
||||||
this.crossAxisDirection = null;
|
this.transform = null;
|
||||||
this.axes = null;
|
|
||||||
|
|
||||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||||
}
|
}
|
||||||
|
@ -337,6 +338,10 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.justifyContentValue = this.computedStyle.justifyContent;
|
this.justifyContentValue = this.computedStyle.justifyContent;
|
||||||
const newJustifyContent = this.justifyContentValue;
|
const newJustifyContent = this.justifyContentValue;
|
||||||
|
|
||||||
|
const oldTransform = this.transformValue;
|
||||||
|
this.transformValue = this.computedStyle.transform;
|
||||||
|
const newTransform = this.transformValue;
|
||||||
|
|
||||||
return hasMoved ||
|
return hasMoved ||
|
||||||
hasFlexDataChanged ||
|
hasFlexDataChanged ||
|
||||||
oldAlignItems !== newAlignItems ||
|
oldAlignItems !== newAlignItems ||
|
||||||
|
@ -344,7 +349,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
oldFlexWrap !== newFlexWrap ||
|
oldFlexWrap !== newFlexWrap ||
|
||||||
oldJustifyContent !== newJustifyContent ||
|
oldJustifyContent !== newJustifyContent ||
|
||||||
oldCrossAxisDirection !== newCrossAxisDirection ||
|
oldCrossAxisDirection !== newCrossAxisDirection ||
|
||||||
oldMainAxisDirection !== newMainAxisDirection;
|
oldMainAxisDirection !== newMainAxisDirection ||
|
||||||
|
oldTransform !== newTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
_hide() {
|
_hide() {
|
||||||
|
@ -530,8 +536,8 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);
|
this.ctx.fillStyle = this.getFlexContainerPattern(devicePixelRatio);
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
drawRect(this.ctx, 0, 0, bounds.width, bounds.height, this.currentMatrix);
|
drawRect(this.ctx, 0, 0, clientWidth, clientHeight, this.currentMatrix);
|
||||||
|
|
||||||
// Find current angle of outer flex element by measuring the angle of two arbitrary
|
// Find current angle of outer flex element by measuring the angle of two arbitrary
|
||||||
// points, then rotate canvas, so the hash pattern stays 45deg to the boundary.
|
// points, then rotate canvas, so the hash pattern stays 45deg to the boundary.
|
||||||
|
@ -556,6 +562,7 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
const zoom = getCurrentZoom(this.win);
|
const zoom = getCurrentZoom(this.win);
|
||||||
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
||||||
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
||||||
|
const containerOffsets = getNodeRect(this.currentNode);
|
||||||
|
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.translate(offset - canvasX, offset - canvasY);
|
this.ctx.translate(offset - canvasX, offset - canvasY);
|
||||||
|
@ -563,21 +570,18 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
this.ctx.lineWidth = lineWidth;
|
this.ctx.lineWidth = lineWidth;
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
for (const flexItem of flexLine.items) {
|
for (const flexItem of flexLine.items) {
|
||||||
const quads = flexItem.quads;
|
const offsets = getNodeRect(flexItem.node);
|
||||||
if (!quads.length) {
|
|
||||||
|
if (!offsets) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the flex item bounds relative to the current quads.
|
const left = offsets.left - containerOffsets.left;
|
||||||
const { bounds: flexItemBounds } = quads[0];
|
const top = offsets.top - containerOffsets.top;
|
||||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
const right = offsets.right - containerOffsets.left;
|
||||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
const bottom = offsets.bottom - containerOffsets.top;
|
||||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
|
||||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
|
||||||
|
|
||||||
clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
clearRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
||||||
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
drawRect(this.ctx, left, top, right, bottom, this.currentMatrix);
|
||||||
|
@ -599,15 +603,14 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
const zoom = getCurrentZoom(this.win);
|
const zoom = getCurrentZoom(this.win);
|
||||||
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
const canvasX = Math.round(this._canvasPosition.x * devicePixelRatio * zoom);
|
||||||
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
const canvasY = Math.round(this._canvasPosition.y * devicePixelRatio * zoom);
|
||||||
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
|
const options = { matrix: this.currentMatrix };
|
||||||
|
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
this.ctx.translate(offset - canvasX, offset - canvasY);
|
this.ctx.translate(offset - canvasX, offset - canvasY);
|
||||||
this.ctx.lineWidth = lineWidth;
|
this.ctx.lineWidth = lineWidth;
|
||||||
this.ctx.strokeStyle = this.color;
|
this.ctx.strokeStyle = this.color;
|
||||||
|
|
||||||
const { bounds } = this.currentQuads.content[0];
|
|
||||||
const options = { matrix: this.currentMatrix };
|
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
const { crossStart, crossSize } = flexLine;
|
const { crossStart, crossSize } = flexLine;
|
||||||
|
|
||||||
|
@ -616,56 +619,56 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
case "horizontal-lr vertical-bt":
|
case "horizontal-lr vertical-bt":
|
||||||
case "horizontal-rl vertical-tb":
|
case "horizontal-rl vertical-tb":
|
||||||
case "horizontal-rl vertical-bt":
|
case "horizontal-rl vertical-bt":
|
||||||
clearRect(this.ctx, 0, crossStart, bounds.width, crossStart + crossSize,
|
clearRect(this.ctx, 0, crossStart, clientWidth, crossStart + crossSize,
|
||||||
this.currentMatrix);
|
this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, 0, crossStart, bounds.width, crossStart, options);
|
drawLine(this.ctx, 0, crossStart, clientWidth, crossStart, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.height - crossStart - crossSize >= lineWidth) {
|
if (clientHeight - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, 0, crossStart + crossSize, bounds.width,
|
drawLine(this.ctx, 0, crossStart + crossSize, clientWidth,
|
||||||
crossStart + crossSize, options);
|
crossStart + crossSize, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "vertical-tb horizontal-lr":
|
case "vertical-tb horizontal-lr":
|
||||||
case "vertical-bt horizontal-rl":
|
case "vertical-bt horizontal-rl":
|
||||||
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, bounds.height,
|
clearRect(this.ctx, crossStart, 0, crossStart + crossSize, clientHeight,
|
||||||
this.currentMatrix);
|
this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, crossStart, 0, crossStart, bounds.height, options);
|
drawLine(this.ctx, crossStart, 0, crossStart, clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.width - crossStart - crossSize >= lineWidth) {
|
if (clientWidth - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, crossStart + crossSize, 0, crossStart + crossSize,
|
drawLine(this.ctx, crossStart + crossSize, 0, crossStart + crossSize,
|
||||||
bounds.height, options);
|
clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "vertical-bt horizontal-lr":
|
case "vertical-bt horizontal-lr":
|
||||||
case "vertical-tb horizontal-rl":
|
case "vertical-tb horizontal-rl":
|
||||||
clearRect(this.ctx, bounds.width - crossStart, 0,
|
clearRect(this.ctx, clientWidth - crossStart, 0,
|
||||||
bounds.width - crossStart - crossSize, bounds.height, this.currentMatrix);
|
clientWidth - crossStart - crossSize, clientHeight, this.currentMatrix);
|
||||||
|
|
||||||
// Avoid drawing the start flex line when they overlap with the flex container.
|
// Avoid drawing the start flex line when they overlap with the flex container.
|
||||||
if (crossStart != 0) {
|
if (crossStart != 0) {
|
||||||
drawLine(this.ctx, bounds.width - crossStart, 0, bounds.width - crossStart,
|
drawLine(this.ctx, clientWidth - crossStart, 0, clientWidth - crossStart,
|
||||||
bounds.height, options);
|
clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid drawing the end flex line when they overlap with the flex container.
|
// Avoid drawing the end flex line when they overlap with the flex container.
|
||||||
if (bounds.width - crossStart - crossSize >= lineWidth) {
|
if (clientWidth - crossStart - crossSize >= lineWidth) {
|
||||||
drawLine(this.ctx, bounds.width - crossStart - crossSize, 0,
|
drawLine(this.ctx, clientWidth - crossStart - crossSize, 0,
|
||||||
bounds.width - crossStart - crossSize, bounds.height, options);
|
clientWidth - crossStart - crossSize, clientHeight, options);
|
||||||
this.ctx.stroke();
|
this.ctx.stroke();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -683,27 +686,27 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const zoom = getCurrentZoom(this.win);
|
const lineWidth = getDisplayPixelRatio(this.win);
|
||||||
const { bounds } = this.currentQuads.content[0];
|
const { clientWidth, clientHeight } = this.currentNode;
|
||||||
|
const containerOffsets = getNodeRect(this.currentNode);
|
||||||
|
|
||||||
// Draw a justify content pattern over the whole flex container.
|
// Draw a justify content pattern over the whole flex container.
|
||||||
this.drawJustifyContent(0, 0, bounds.width, bounds.height, this.currentMatrix);
|
this.drawJustifyContent(0, 0, clientWidth, clientHeight);
|
||||||
|
|
||||||
for (const flexLine of this.flexData.lines) {
|
for (const flexLine of this.flexData.lines) {
|
||||||
const { crossStart, crossSize } = flexLine;
|
const { crossStart, crossSize } = flexLine;
|
||||||
|
|
||||||
for (const flexItem of flexLine.items) {
|
for (const flexItem of flexLine.items) {
|
||||||
const quads = flexItem.quads;
|
const offsets = getNodeRect(flexItem.node);
|
||||||
if (!quads.length) {
|
|
||||||
|
if (!offsets) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjust the flex item bounds relative to the current quads.
|
const left = offsets.left - containerOffsets.left;
|
||||||
const { bounds: flexItemBounds } = quads[0];
|
const top = offsets.top - containerOffsets.top;
|
||||||
const left = Math.round(flexItemBounds.left / zoom - bounds.left);
|
const right = offsets.right - containerOffsets.left;
|
||||||
const top = Math.round(flexItemBounds.top / zoom - bounds.top);
|
const bottom = offsets.bottom - containerOffsets.top;
|
||||||
const right = Math.round(flexItemBounds.right / zoom - bounds.left);
|
|
||||||
const bottom = Math.round(flexItemBounds.bottom / zoom - bounds.top);
|
|
||||||
|
|
||||||
// Clear a rectangular are covering the alignment container.
|
// Clear a rectangular are covering the alignment container.
|
||||||
switch (this.axes) {
|
switch (this.axes) {
|
||||||
|
@ -711,18 +714,22 @@ class FlexboxHighlighter extends AutoRefreshHighlighter {
|
||||||
case "horizontal-lr vertical-bt":
|
case "horizontal-lr vertical-bt":
|
||||||
case "horizontal-rl vertical-tb":
|
case "horizontal-rl vertical-tb":
|
||||||
case "horizontal-rl vertical-bt":
|
case "horizontal-rl vertical-bt":
|
||||||
clearRect(this.ctx, left, Math.round(crossStart) + 2, right,
|
clearRect(this.ctx,
|
||||||
Math.round(crossStart + crossSize) - 2, this.currentMatrix);
|
left, Math.round(crossStart) + 2 * lineWidth, right,
|
||||||
|
Math.round(crossStart + crossSize) - 2 * lineWidth, this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
case "vertical-tb horizontal-lr":
|
case "vertical-tb horizontal-lr":
|
||||||
case "vertical-bt horizontal-rl":
|
case "vertical-bt horizontal-rl":
|
||||||
clearRect(this.ctx, Math.round(crossStart) + 1, top,
|
clearRect(this.ctx,
|
||||||
Math.round(crossStart + crossSize), bottom, this.currentMatrix);
|
Math.round(crossStart) + lineWidth * 2, top,
|
||||||
|
Math.round(crossStart + crossSize) - lineWidth, bottom, this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
case "vertical-bt horizontal-lr":
|
case "vertical-bt horizontal-lr":
|
||||||
case "vertical-tb horizontal-rl":
|
case "vertical-tb horizontal-rl":
|
||||||
clearRect(this.ctx, Math.round(bounds.width - crossStart - crossSize) + 1,
|
clearRect(this.ctx,
|
||||||
top, Math.round(bounds.width - crossStart), bottom, this.currentMatrix);
|
Math.round(clientWidth - crossStart - crossSize) + lineWidth * 2, top,
|
||||||
|
Math.round(clientWidth - crossStart) - lineWidth, bottom,
|
||||||
|
this.currentMatrix);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -888,4 +895,55 @@ function compareFlexData(oldFlexData, newFlexData) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the untransformed coordinates for a node.
|
||||||
|
*
|
||||||
|
* @param {DOMNode} node
|
||||||
|
* The node for which the coordinates are to be returned.
|
||||||
|
*
|
||||||
|
* @returns {Object}
|
||||||
|
* {
|
||||||
|
* left: left, // The absolute left coordinates of the node.
|
||||||
|
* top: top, // The absolute top coordinates of the node.
|
||||||
|
* right: right, // The absolute right coordinates of the node.
|
||||||
|
* bottom: bottom, // The absolute left coordinates of the node.
|
||||||
|
* width: width, // The width of the node.
|
||||||
|
* height, // The Height of the node.
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function getNodeRect(node) {
|
||||||
|
if (node.nodeType === node.TEXT_NODE) {
|
||||||
|
// For now ignore text node flex items because we cannot get the
|
||||||
|
// untransformed position and dimensions of a text node
|
||||||
|
// (see https://bugzil.la/1505079).
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const win = node.ownerGlobal;
|
||||||
|
const style = win.getComputedStyle(node);
|
||||||
|
const borderLeft = parseInt(style.borderLeftWidth, 10) || 0;
|
||||||
|
const borderTop = parseInt(style.borderTopWidth, 10) || 0;
|
||||||
|
const width = node.offsetWidth;
|
||||||
|
const height = node.offsetHeight;
|
||||||
|
|
||||||
|
let left = 0;
|
||||||
|
let top = 0;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
left += node.offsetLeft - node.scrollLeft + node.clientLeft;
|
||||||
|
top += node.offsetTop - node.scrollTop + node.clientTop;
|
||||||
|
|
||||||
|
node = node.offsetParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
left: left - borderLeft,
|
||||||
|
top: top - borderTop,
|
||||||
|
right: left + width - borderLeft,
|
||||||
|
bottom: top + height - borderTop,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
exports.FlexboxHighlighter = FlexboxHighlighter;
|
exports.FlexboxHighlighter = FlexboxHighlighter;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче