Mo0*_*rBy 3 javascript css svg reactjs
我正在尝试开发一款基于“五度循环”的游戏,这是一个音乐概念。为了使其正确,我将元素旋转了 15 度,以便圆的最顶部位于第一段的中间,而不是第一段的开头(您可能会说它旋转了 -15 度,但无论如何) 。
我现在面临的问题是我无法根据需要旋转圆段内的文本。每个段都是一个<g>带有子元素<path>和<text>嵌套在其中的子元素的元素。目前,我只是使用该rotate属性,这对于单个字符或 2 个字符来说是可以的,但除此之外,它看起来不正确。该属性的工作方式如下rotate所述(“旋转每个单独字形的方向。可以单独旋转字形。”)。我想旋转整个元素,以便所有字符都旋转到相同的角度,并且看起来呈现在同一条线上,垂直于字母的旋转。<text>
我尝试使用transform=rotate(15)和的组合text-anchor: middle而不是使用rotate属性,但看起来好像它也在 X 和 Y 坐标中移动元素。我也尝试过使用该dominant-baseline属性,但我的 VSCode 指出该属性不存在glyph-orientation-horizontal,并且该属性据我所知没有执行任何操作。
这是我的代码(我正在使用 Reactjs,所以我只是为您提供可以非常轻松地放入 App.js 中的组件代码以及组件的 CSS 代码(顺便说一句,有几行 D3,只是为了计算路径弧))以及一些屏幕截图,一张图像没有使用该rotate属性,另一张图像使用了该rotate属性。
组件和 CSS 代码:
// My component
import './CircleOfFifths.css';
import { useEffect, useState } from 'react';
import * as d3 from 'd3';
import musicKeys from '../../MusicKeys';
export default function CircleOfFifths({ outerRadius }) {
const diameter = outerRadius * 2;
const innerRadius = outerRadius * 0.7;
const innerRadius2 = outerRadius * 0.4;
const [musicKeysObject, setMusicKeysObject] = useState(musicKeys);
useEffect(() => {
let newMusicKeysObject = musicKeysObject;
for (let i=0; i<newMusicKeysObject.length; i++) {
newMusicKeysObject[i].segmentMetadata.majorCircle.isVisible = true;
newMusicKeysObject[i].segmentMetadata.minorCircle.isVisible = true;
}
setMusicKeysObject([...newMusicKeysObject]);
}, [])
const majorOnMouseUpHandler = (index) => {
let newMusicKeysObject = musicKeysObject;
if (newMusicKeysObject[index].segmentMetadata.majorCircle.isVisible) {
newMusicKeysObject[index].segmentMetadata.majorCircle.isVisible = false;
} else {
newMusicKeysObject[index].segmentMetadata.majorCircle.isVisible = true;
}
setMusicKeysObject([...newMusicKeysObject]);
}
const minorOnMouseUpHandler = (index) => {
let newMusicKeysObject = musicKeysObject;
if (newMusicKeysObject[index].segmentMetadata.minorCircle.isVisible) {
newMusicKeysObject[index].segmentMetadata.minorCircle.isVisible = false;
} else {
newMusicKeysObject[index].segmentMetadata.minorCircle.isVisible = true;
}
setMusicKeysObject([...newMusicKeysObject]);
}
const calculateArc = (innerRadius, outerRadius, startAngle, endAngle) => {
return d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.startAngle(startAngle)
.endAngle(endAngle);
}
const renderMajorSegment = (musicKey, index) => {
let arc = calculateArc(outerRadius, innerRadius, musicKey.segmentMetadata.startAngle, musicKey.segmentMetadata.endAngle);
let [arcCenterX, arcCenterY] = arc.centroid();
return <g className={`circle-segment ${musicKey.segmentMetadata.majorCircle.isVisible ? 'isVisible': ''}`} key={index} onMouseUp={() => majorOnMouseUpHandler(index)}>
<path
d={arc.apply()} // apply() is needed to generate the string that goes into the 'd' attribute
/>
{/* Need to adjust center X position due to increased font-size */}
<text x={arcCenterX-10} y={arcCenterY} rotate={15}>
{musicKey.chords[0].replace('Major', '')}
</text>
</g>
}
const renderMinorSegment = (musicKey, index) => {
let arc = calculateArc(innerRadius, innerRadius2, musicKey.segmentMetadata.startAngle, musicKey.segmentMetadata.endAngle);
let [arcCenterX, arcCenterY] = arc.centroid();
return <g className={`circle-segment ${musicKey.segmentMetadata.minorCircle.isVisible ? 'isVisible': ''}`} key={index} onMouseUp={() => minorOnMouseUpHandler(index)}>
<path
d={arc.apply()} // apply() is needed to generate the string that goes into the 'd' attribute
/>
{/* Need to adjust center X position due to increased font-size */}
<text x={arcCenterX-10} y={arcCenterY} rotate={15}>
{musicKey.chords[5].replace(' minor', 'mgh')}
</text>
</g>
}
return (
<div className='circle-of-fifths-container'>
<svg
height={diameter*1.1}
width={diameter*1.1}>
<g className='circle-container' transform={`translate(${diameter/2 + 25},${diameter/2 + 25}) rotate(-15)`}>
<circle r={outerRadius} className="base-circle"/>
<g className='outer-circle-segments-container'>
{musicKeysObject.map((musicKey, index) => renderMajorSegment(musicKey, index))}
</g>
<g className='inner-circle-segments-container'>
{musicKeysObject.map((musicKey, index) => renderMinorSegment(musicKey, index))}
</g>
</g>
</svg>
</div>
)
}
// It's CSS
.circle-of-fifths-container {
margin: 50px;
text-align: center;
}
.circle-container {
text-align: center;
}
.base-circle {
fill: grey;
padding: 5px;
border: 5px;
margin: 5px;
stroke: grey;
stroke-width: 5px;
}
.circle-segment {
visibility: none;
}
.circle-segment.isVisible {
visibility: visible;
> path {
fill: #D7DCDF;
stroke: black;
stroke-width: 5px;
}
> text {
font-size: 30px;
text-anchor: initial;
}
}
Run Code Online (Sandbox Code Playgroud)
让\xe2\x80\x99s 回顾一下 (a) SVG 中对象如何旋转以及 (b) SVG 中文本如何定位。我们从一个简单的正方形开始:
\n<svg viewBox="0 0 60 60">\n <rect x="15" y="15" width="30" height="30"/>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们\xe2\x80\x99将使用以下命令旋转它transform=rotate(45):
<svg viewBox="0 0 60 60">\n <rect x="15" y="15" width="30" height="30" transform="rotate(45)"/>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n正方形已旋转,但围绕原点\xe2\x80\x93 坐标 (0,0) \xe2\x80\x93 旋转,在本例中为视图框的左上角。默认情况下,SVG 中的旋转是围绕原点执行的。如果我们想要围绕任何其他点旋转对象,我们需要将这些坐标指定为旋转变换的第二个和第三个参数。我们想要围绕正方形的中心(位于点 (30,30))旋转,因此我们的变换需要为transform=rotate(45, 30, 30)。
<svg viewBox="0 0 60 60">\n <rect x="15" y="15" width="30" height="30" transform="rotate(45, 30, 30)"/>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n好吧,魔法!这就是我们想要的\xe2\x80\x99!现在让\xe2\x80\x99s 尝试一些文本,我们\xe2\x80\x99 将其放置在视口的中心(30,30)。
\n<svg viewBox="0 0 60 60">\n <text x="30" y="30">fred</text>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n因此,默认情况下,x 坐标指定文本的开头,y 坐标指定其基线。那\xe2\x80\x99不是我们想要的。我们希望两个坐标都指定文本的中心。我们可以通过设置text-anchor="middle"水平对齐和dominant-baseline="central"垂直对齐来做到这一点。
<svg viewBox="0 0 60 60">\n <text x="30" y="30" text-anchor="middle" dominant-baseline="central">fred</text>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n是的!现在\xe2\x80\x99 绕(30,30) 旋转就可以得到想要的结果了。
\n<svg viewBox="0 0 60 60">\n <text x="30" y="30" text-anchor="middle" dominant-baseline="central" transform="rotate(45, 30, 30)">fred</text>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\n\n<svg viewBox="0 0 60 60">\n <rect x="15" y="15" width="30" height="30"/>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\r\n<svg viewBox="0 0 60 60">\n <rect x="15" y="15" width="30" height="30" transform="rotate(45)"/>\n</svg>\nRun Code Online (Sandbox Code Playgroud)\r\n所以现在,希望您可以采用这些 SVG 概念并在您的 React 代码中实现它们。
\n注意 1. SVG 视图框\xe2\x80\x99 不一定具有左上角的原点。您可以设置一个视图框,使\n(0,0) 位于中心,例如:
\n<svg viewBox="-30 -30 60 60">\nRun Code Online (Sandbox Code Playgroud)\n注 2:SVG 变换也可以设置动画。请参阅上面的代码片段作为示例,MDN是一个不错的文档起点。
\n| 归档时间: |
|
| 查看次数: |
390 次 |
| 最近记录: |