gpt4 book ai didi

javascript - 如何在透视模式下计算旋转 Angular 以使宽度适合所需尺寸?

转载 作者:行者123 更新时间:2023-11-30 13:55:12 24 4
gpt4 key购买 nike

我正在尝试想出一种方法,通过 CSS 以透视方式围绕 Y 轴旋转图像,以便最终可见宽度等于所需的像素数。

例如,我可能想旋转一张 300px 图像,以便在应用旋转和透视后,图像的宽度现在为 240px(80%原来的)。通过反复试验,我知道我可以设置 transform: perspective(300) rotateY(-12.68) 并将左上角点放在 -240px (这是使用右图像的一侧作为原点)

我不太清楚如何对其进行逆向工程,以便对于任何给定的图像宽度、透视和所需宽度,我都可以计算出必要的旋转。

例如。对于同一张 300px 图像,我现在希望它在旋转后宽度为 150px - 获得必要 Angular 所需的计算是什么?

这里有一个 playground,可以让您了解我在寻找什么,我已经复制了透视和旋转变换所做的数学计算来计算最左边点的最终位置,但我还没有能够在给定矩阵数学和涉及的多个步骤的情况下找出如何求解 Angular 。

https://repl.it/@BenSlinger/PerspectiveWidthDemo

const calculateLeftTopPointAfterTransforms = (perspective, rotation, width) => {

// convert degrees to radians
const rRad = rotation * (Math.PI / 180);

// place the camera
const cameraMatrix = math.matrix([0, 0, -perspective]);

// get the upper left point of the image based on middle right transform origin
const leftMostPoint = math.matrix([-width, -width / 2, 0]);

const rotateYMatrix = math.matrix([
[Math.cos(-rRad), 0, -Math.sin(-rRad)],
[0, 1, 0],
[Math.sin(-rRad), 0, Math.cos(-rRad)],
]);

// apply rotation to point
const rotatedPoint = math.multiply(rotateYMatrix, leftMostPoint);

const cameraProjection = math.subtract(rotatedPoint, cameraMatrix);

const pointInHomogenizedCoords = math.multiply(math.matrix([
[1, 0, 0 / perspective, 0],
[0, 1, 0 / perspective, 0],
[0, 0, 1, 0],
[0, 0, 1 / perspective, 0],
]), cameraProjection.resize([4], 1));

const finalPoint = [
math.subset(pointInHomogenizedCoords, math.index(0))
/ math.subset(pointInHomogenizedCoords, math.index(3)),
math.subset(pointInHomogenizedCoords, math.index(1))
/ math.subset(pointInHomogenizedCoords, math.index(3)),
];

return finalPoint;
}
<div id="app"></div>


<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.js"></script>

<script type="text/babel" data-plugins="transform-class-properties" >
// GOAL: Given the percentage defined in desiredWidth, calculate the rotation required for the transformed image to fill that space (shown by red background)

// eg: With desiredWidth 80 at perspective 300 and image size 300, rotation needs to be 12.68, putting the left point at 300 * .8 = 240.
// How do I calculate that rotation for any desired width, perspective and image size?


// factor out some styles
const inputStyles = { width: 50 };

const PerspDemo = () => {

const [desiredWidth, setDesiredWidth] = React.useState(80);
const [rotation, setRotation] = React.useState(25);
const [perspective, setPerspective] = React.useState(300);
const [imageSize, setImageSize] = React.useState(300);
const [transformedPointPosition, setTPP] = React.useState([0, 0]);

const boxStyles = { outline: '1px solid red', width: imageSize + 'px', height: imageSize + 'px', margin: '10px', position: 'relative' };

React.useEffect(() => {
setTPP(calculateLeftTopPointAfterTransforms(perspective, rotation, imageSize))
}, [rotation, perspective]);


return <div>
<div>
<label>Image size</label>
<input
style={inputStyles}
type="number"
onChange={(e) => setImageSize(e.target.value)}
value={imageSize}
/>
</div>
<div>
<label>Desired width after transforms (% of size)</label>
<input
style={inputStyles}
type="number"
onChange={(e) => setDesiredWidth(e.target.value)}
value={desiredWidth}
/>
</div>

<div>
<label>Rotation (deg)</label>
<input
style={inputStyles}
type="number"
onChange={(e) => setRotation(e.target.value)}
value={rotation}
/>
</div>

<div>
<label>Perspective</label>
<input
style={inputStyles}
type="number"
onChange={(e) => setPerspective(e.target.value)}
value={perspective}
/>
</div>



<div>No transforms:</div>
<div style={boxStyles}>
<div>
<img src={`https://picsum.photos/${imageSize}/${imageSize}`} />
</div>
</div>

<div>With rotation and perspective:</div>
<div style={boxStyles}>
<div style={{ display: 'flex', position: 'absolute', height: '100%', width: '100%' }}>
<div style={{ backgroundColor: 'white', flexBasis: 100 - desiredWidth + '%' }} />
<div style={{ backgroundColor: 'red', flexGrow: 1 }} />

</div>
<div style={{
transform: `perspective(${perspective}px) rotateY(-${rotation}deg)`,
transformOrigin: '100% 50% 0'
}}>
<img src={`https://picsum.photos/${imageSize}/${imageSize}`} />
</div>
</div>
<div>{transformedPointPosition.toString()}</div>
</div>;
};

ReactDOM.render(<PerspDemo />, document.getElementById('app'));

</script>


<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/6.0.4/math.min.js"></script>

非常感谢任何帮助!

最佳答案

我会考虑一种不同的方法来找到没有矩阵计算的公式1 以获得以下内容:

R = (p * cos(angle) * D)/(p - (sin(angle) * D))

其中p是透视,angle是 Angular 旋转,D是元素宽度,R是我们要搜索的新宽度。

如果我们有 -45deg 的 Angular 和等于 100px 的视角以及初始宽度 200px 那么新的宽度将为: 58.58px

.box {
width: 200px;
height: 200px;
border: 1px solid;
background:
linear-gradient(red,red) right/58.58px 100% no-repeat;
position:relative;
}

img {
transform-origin:right;
}
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform:perspective(100px) rotateY(-45deg)">
</div>

如果我们有 -30deg 的 Angular 和等于 200px 的视角和初始宽度 200px 那么新的宽度将是 115.46px

.box {
width: 200px;
height: 200px;
border: 1px solid;
background:
linear-gradient(red,red) right/115.46px 100% no-repeat;
position:relative;
}

img {
transform-origin:right;
}
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform:perspective(200px) rotateY(-30deg)">
</div>

1 为了更好地理解该公式,让我们考虑下图:

enter image description here

想象一下,我们正在从顶部查看所有内容。红线是我们旋转的元素。大黑点是我们的视角,与场景的距离等于 p(这是我们的视角)。由于 transform-origin 是正确的,因此将此点放在正确的位置是合乎逻辑的。否则,它应该在中心。

现在,我们看到的是R设计的宽度,W是我们在没有透视的情况下看到的宽度。很明显,大视角下我们看到的几乎和没有大视角的一样

.box {
width: 200px;
height: 200px;
border: 1px solid;
}

img {
transform-origin:right;
}
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform: rotateY(-30deg)">
</div>
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform:perspective(9999px) rotateY(-30deg)">
</div>

从小 Angular 看,我们看到的宽度很小

.box {
width: 200px;
height: 200px;
border: 1px solid;
}

img {
transform-origin:right;
}
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform: rotateY(-30deg)">
</div>
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform:perspective(15px) rotateY(-30deg)">
</div>

如果考虑图中O标注的 Angular ,我们可以写出如下公式:

tan(O) = R/p

tan(O) = W/(L + p)

所以我们将有 R = p*W/(L + p)W = cos(-angle)*D = cos(angle)*DL = sin(-angle)*D = -sin(angle)*D 这将给我们:

R = (p * cos(angle) * D)/(p - (sin(angle) * D))

为了找到 Angular ,我们可以将公式转换为:

R*p - R*D*sin(angle) = p*D*cos(angle)
R*p = D*(p*cos(angle) + R*sin(angle))

然后像描述的那样here 1我们可以得到如下等式:

angle = sin-1((R*p)/(D*sqrt(p²+R²))) - tan-1(p/R)

如果你想要一个等于 190px 和 R 等于 150pxD 等于 200px 你需要等于 -15.635deg

的旋转

.box {
width: 200px;
height: 200px;
border: 1px solid;
background:
linear-gradient(red,red) right/150px 100% no-repeat;
position:relative;
}

img {
transform-origin:right;
}
<div class="box">
<img src="https://picsum.photos/id/1/200/200" style="transform:perspective(190px) rotateY(-15.635deg)">
</div>


1 感谢 https://math.stackexchange.com帮助我确定正确公式的社区

关于javascript - 如何在透视模式下计算旋转 Angular 以使宽度适合所需尺寸?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57431060/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com