我正在try 开发一款基于音乐概念的《五人圈》的游戏.为了纠正错误,我将元素旋转了15度,使圆的最顶端位于第一段的中间,而不是第一段的起点(您可能会争辩说它旋转了-15度,但不管怎样).
我现在面临的问题是,我不能按要求在我的圆圈段内旋转文本.每个段是一个<g>
元素,其内嵌套子元素<path>
和子元素<text>
.目前,我只使用rotate
属性,这对于单个字符或2个字符来说是可以的,但如果超过这个范围,它看起来就不正确了.rotate
属性的作用如描述here所示("旋转每个单独字形的方向.可以单独旋转字形.").我想要旋转整个<text>
元素,以便所有字符旋转到相同的Angular AND看起来呈现在同一条线上,垂直于字母的旋转.
我try 使用transform=rotate(15)
和text-anchor: middle
的组合,而不是使用rotate
属性,但看起来它似乎也在X和Y坐标中移动元素.我也try 过使用dominant-baseline
属性,但我的VSCode声明该属性不存在,而glyph-orientation-horizontal
属性,据我所知没有任何作用.
以下是我的代码(我使用的是Reactjs,所以我只是给您提供了可以非常轻松地放入App.js中的组件代码和组件的css代码(有几行D3btw,只是为了计算路径弧)),以及一些屏幕截图,一个图像不使用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;
}
}