要はRed Hot Chili Peppersのロゴのテキストみたいなやつを同じテキストをリピートさせてやりたい。
探してみたところこのあたりが引っかかった:
- https://css-tricks.com/set-text-on-a-circle/
- https://stackoverflow.com/questions/23811276/wrapping-a-text-around-a-circular-element
なるほど。どうにもシンプルにやる方法はなさそう。
しかし、SVGのtext要素はPathに沿って並べることができることはわかった。
https://developer.mozilla.org/ja/docs/Web/SVG/Element/text
SVGは現状だいたいのブラウザで対応しているっぽいしいけそう。
https://caniuse.com/#search=svg
円を描くPathを動的に生成できれば楽に自動化できそうだ。すでに書いているひとがいた。
https://tyru.github.io/svg-circle-misc-algorithm/
拝借してゴニョったところできた。やったね。
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
:viewBox="viewBox"
:style="{ maxWidth: boxSize + 'px' }"
>
<title v-text="text" />
<defs>
<path
id="textcircle"
:d="circlePath"
/>
</defs>
<text :font-size="fontSize">
<textPath xlink:href="#textcircle" v-text="repeatedAroundText" />
</text>
</svg>
</template>
<script>
/*
* thanks: https://tyru.github.io/svg-circle-misc-algorithm/
*/
const SEGMENTS = 8
const ANGLE = 2 * Math.PI / SEGMENTS
const HALF_ANGLE_RATIO = Math.tan(ANGLE / 2)
function range(from, to) {
const length = to - from
return Array.from({ length: length + 1 }, (v, k) => k + from)
}
function drawCirclePath(center, radius) {
const anchorX = theta => radius * Math.cos(theta)
const anchorY = theta => radius * Math.sin(theta)
const controlX = theta => anchorX(theta) + radius * HALF_ANGLE_RATIO * Math.cos(theta - Math.PI / 2)
const controlY = theta => anchorY(theta) + radius * HALF_ANGLE_RATIO * Math.sin(theta - Math.PI / 2)
return [
['M', center + radius, center],
...range(1, SEGMENTS).map((index) => {
const theta = index * ANGLE
return ['Q', controlX(theta) + center, controlY(theta) + center, anchorX(theta) + center, anchorY(theta) + center]
})
]
}
export default {
props: {
text: {
type: String,
default: ''
},
fontSize: {
type: Number,
default: 12
},
boxSize: {
type: Number,
default: 800
}
},
data() {
return {}
},
computed: {
centerPosition() {
return Math.floor(this.boxSize / 2)
},
radius() {
return Math.floor(this.boxSize / 2) - this.fontSize * 2
},
viewBox() {
return [0, 0, this.boxSize, this.boxSize].join(' ')
},
repeatedAroundText() {
const repeatCount = Math.ceil((this.radius * 2 * Math.PI) / this.fontSize / this.text.length)
return this.text.repeat(repeatCount)
},
circlePath() {
const circlePath = drawCirclePath(this.centerPosition, this.radius)
return circlePath.map(path => path[0] + path.slice(1).join(',')).join(' ')
}
}
}
</script>
SVGそのものの描画コストはSVGのソースも小さいし問題ないと信じてる。
このへんのDOM生成が重くなってもNuxt.jsでSSRしてやったりすればよさそう。
ちょっと気になる記事があったけど、等幅配置の際の問題のようなのでこれには影響なさそう。
https://qiita.com/MKLemma/items/c38cd2587a1451a95e3e
もちろん、別のブラウザ依存の問題がないとも言えないので、その後は要調査。