【发布时间】:2019-09-03 09:32:03
【问题描述】:
只是为了好玩,我正在尝试在 3D 转换的画布上绘画。我写了一些代码,它有点工作
const m4 = twgl.m4;
[...document.querySelectorAll('canvas')].forEach((canvas) => {
const ctx = canvas.getContext('2d');
let count = 0;
canvas.addEventListener('mousemove', (e) => {
const pos = getElementRelativeMousePosition(e, canvas);
ctx.fillStyle = hsl((count++ % 10) / 10, 1, 0.5);
ctx.fillRect(pos.x - 1, pos.y - 1, 3, 3);
});
});
function getElementRelativeMousePosition(e, elem) {
const pos = convertPointFromPageToNode(elem, e.pageX, e.pageY);
return {
x: pos[0],
y: pos[1],
};
}
function hsl(h, s, l) {
return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
function convertPointFromPageToNode(elem, pageX, pageY) {
const mat = m4.inverse(getTransformationMatrix(elem));
return m4.transformPoint(mat, [pageX, pageY, 0]);
};
function getTransformationMatrix(elem) {
let matrix = m4.identity();
let currentElem = elem;
while (currentElem !== undefined &&
currentElem !== currentElem.ownerDocument.documentElement) {
const style = window.getComputedStyle(currentElem);
const localMatrix = parseMatrix(style.transform);
matrix = m4.multiply(localMatrix, matrix);
currentElem = currentElem.parentElement;
}
const w = elem.offsetWidth;
const h = elem.offsetHeight;
let i = 4;
let left = +Infinity;
let top = +Infinity;
for (let i = 0; i < 4; ++i) {
const p = m4.transformPoint(matrix, [w * (i & 1), h * ((i & 2) >> 1), 0]);
left = Math.min(p[0], left);
top = Math.min(p[1], top);
}
const rect = elem.getBoundingClientRect()
document.querySelector('p').textContent =
`${w}x${h}`;
matrix = m4.multiply(m4.translation([
window.pageXOffset + rect.left - left,
window.pageYOffset + rect.top - top,
0]), matrix);
return matrix;
}
function parseMatrix(str) {
if (str.startsWith('matrix3d(')) {
return str.substring(9, str.length - 1).split(',').map(v => parseFloat(v.trim()));
} else if (str.startsWith('matrix(')) {
const m = str.substring(7, str.length - 1).split(',').map(v => parseFloat(v.trim()));
return [
m[0], m[1], 0, 0,
m[2], m[3], 0, 0,
0, 0, 1, 0,
m[4], m[5], 0, 1,
]
} else if (str == 'none') {
return m4.identity();
}
throw new Error('unknown format');
}
canvas {
display: block;
background: yellow;
transform: scale(0.75);
}
#c1 {
margin: 20px;
background: red;
transform: translateX(-50px);
display: inline-block;
}
#c2 {
margin: 20px;
background: green;
transform: rotate(45deg);
display: inline-block;
}
#c3 {
margin: 20px;
background: blue;
display: inline-block;
}
#c4 {
position: absolute;
top: 0;
background: cyan;
transform: translateX(-250px) rotate(55deg);
display: inline-block;
}
#c5 {
background: magenta;
transform: translate(50px);
display: inline-block;
}
#c6 {
background: pink;
transform: rotate(45deg);
display: inline-block;
}
<p>
foo
</p>
<div id="c1">
<div id="c2">
<div id="c3">
<canvas></canvas>
</div>
</div>
</div>
<div id="c4">
<div id="c5">
<div id="c6">
<canvas></canvas>
</div>
</div>
</div>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
上面的代码有效。将鼠标移到任一黄色画布元素上,您会看到它正确绘制。
但是,一旦我添加了一些 3D 变换,它就会失败。
将“#c6”的 CSS 更改为
#c6 {
background: pink;
transform: rotate(45deg) rotateX(45deg); /* changed */
display: inline-block;
}
现在当我在右边的黄色画布上绘制时,一切都关闭了。
const m4 = twgl.m4;
[...document.querySelectorAll('canvas')].forEach((canvas) => {
const ctx = canvas.getContext('2d');
let count = 0;
canvas.addEventListener('mousemove', (e) => {
const pos = getElementRelativeMousePosition(e, canvas);
ctx.fillStyle = hsl((count++ % 10) / 10, 1, 0.5);
ctx.fillRect(pos.x - 1, pos.y - 1, 3, 3);
});
});
function getElementRelativeMousePosition(e, elem) {
const pos = convertPointFromPageToNode(elem, e.pageX, e.pageY);
return {
x: pos[0],
y: pos[1],
};
}
function hsl(h, s, l) {
return `hsl(${h * 360 | 0},${s * 100 | 0}%,${l * 100 | 0}%)`;
}
function convertPointFromPageToNode(elem, pageX, pageY) {
const mat = m4.inverse(getTransformationMatrix(elem));
return m4.transformPoint(mat, [pageX, pageY, 0]);
};
function getTransformationMatrix(elem) {
let matrix = m4.identity();
let currentElem = elem;
while (currentElem !== undefined &&
currentElem !== currentElem.ownerDocument.documentElement) {
const style = window.getComputedStyle(currentElem);
const localMatrix = parseMatrix(style.transform);
matrix = m4.multiply(localMatrix, matrix);
currentElem = currentElem.parentElement;
}
const w = elem.offsetWidth;
const h = elem.offsetHeight;
let i = 4;
let left = +Infinity;
let top = +Infinity;
for (let i = 0; i < 4; ++i) {
const p = m4.transformPoint(matrix, [w * (i & 1), h * ((i & 2) >> 1), 0]);
left = Math.min(p[0], left);
top = Math.min(p[1], top);
}
const rect = elem.getBoundingClientRect()
document.querySelector('p').textContent =
`${w}x${h}`;
matrix = m4.multiply(m4.translation([
window.pageXOffset + rect.left - left,
window.pageYOffset + rect.top - top,
0]), matrix);
return matrix;
}
function parseMatrix(str) {
if (str.startsWith('matrix3d(')) {
return str.substring(9, str.length - 1).split(',').map(v => parseFloat(v.trim()));
} else if (str.startsWith('matrix(')) {
const m = str.substring(7, str.length - 1).split(',').map(v => parseFloat(v.trim()));
return [
m[0], m[1], 0, 0,
m[2], m[3], 0, 0,
0, 0, 1, 0,
m[4], m[5], 0, 1,
]
} else if (str == 'none') {
return m4.identity();
}
throw new Error('unknown format');
}
canvas {
display: block;
background: yellow;
transform: scale(0.75);
}
#c1 {
margin: 20px;
background: red;
transform: translateX(-50px);
display: inline-block;
}
#c2 {
margin: 20px;
background: green;
transform: rotate(45deg);
display: inline-block;
}
#c3 {
margin: 20px;
background: blue;
display: inline-block;
}
#c4 {
position: absolute;
top: 0;
background: cyan;
transform: translateX(-250px) rotate(55deg);
display: inline-block;
}
#c5 {
background: magenta;
transform: translate(50px);
display: inline-block;
}
#c6 {
background: pink;
transform: rotate(45deg) rotateX(45deg);
display: inline-block;
}
<p>
foo
</p>
<div id="c1">
<div id="c2">
<div id="c3">
<canvas></canvas>
</div>
</div>
</div>
<div id="c4">
<div id="c5">
<div id="c6">
<canvas></canvas>
</div>
</div>
</div>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
任何想法我做错了什么?
【问题讨论】:
-
混合 CSS 转换和 Canvas 的坏主意...使用适当的 3D 框架
-
有了一个好的框架,你会获得更多的乐趣,很多“平凡的东西”都被抽象出来了
-
知道了,所以你知道自己的方式......我会开始将示例简化为不那么具有威胁性的东西,300行代码很可怕
-
有趣。没有时间真正深入检查会发生什么(不确定是否可以),但有两个小观察。 IIRC getBoundingClientRect 在 3d 转换元素上返回一个奇怪的东西,这可能与您的问题相关,也可能不相关。当你找到元素的
.offsetParent时,你会想要停止迭代,这现在不是问题,但将来可能会成为问题。 -
哦,这不是问题所要求的,但是如果您想要一个hack-around,您可以在您的绘图区域上放置一个HTML元素网格作为事件侦听器。 Cwidth Cheight 元素,可以是 1px x 高度或 1px x 宽度,然后检查哪些是悬停的。实际上,您检查过DOMPoint API 吗?我自己从未使用过它,但在这里可能会有所帮助。
标签: javascript html css canvas css-transforms