【发布时间】:2019-12-17 06:06:54
【问题描述】:
我试图想出一种方法,通过 CSS 围绕 Y 轴透视旋转图像,以使最终的可见宽度等于所需的像素数。
例如,我可能想要旋转300px 图像,以便在应用旋转和透视后,图像的宽度现在为240px(原始宽度的80%)。通过反复试验,我知道我可以设置 transform: perspective(300) rotateY(-12.68) 并将左上角置于 -240px (这是使用图像的右侧作为原点)
我不太清楚如何对此进行逆向工程,以便对于任何给定的图像宽度、透视图和所需宽度,我都可以计算出必要的旋转。
例如。对于相同的300px 图像,我现在希望它在旋转后的宽度为150px - 获得必要角度所需的计算是什么?
这是一个让您了解我正在寻找什么的游乐场,我已经复制了透视和旋转变换完成的数学运算来计算最左边点的最终位置,但我没有在给出矩阵数学和涉及的多个步骤的情况下,能够弄清楚如何求解角度。
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>
非常感谢任何帮助!
【问题讨论】:
-
如果你知道数学规则,你可以计算新的宽度并用js设置它。试试看这个:stackoverflow.com/a/46107713/11368483也许你可以在数学专业社区问
-
@red,对不起,我最初的问题一定不够清楚。我知道如何求解宽度,实际上我正在尝试求解角度,以便生成的图像具有给定的宽度。我已经编辑了问题以澄清。 (我尝试在 math.stackexchange.com 上提问,但被认为是题外话)
标签: javascript css math css-transforms perspective