demo image

demo image

<template>
<div>
<canvas ref="canvas" :width="canvasWidth" :height="canvasHeight"></canvas>
</div>
</template>

<script>
export default {
props: {
text: {
default: '阿斯顿发斯蒂芬',
type: String,
required: true
},
radius: {
type: Number,
default: 200 // 控制文字弯曲的半径
},
canvasWidth: {
type: Number,
default: 500 // Canvas 宽度
},
canvasHeight: {
type: Number,
default: 600 // Canvas 高度
},
fontSize: {
type: Number,
default: 32 // 字体大小
},
curvature: {
type: Number,
default: 0.5 // 弯曲度,可以通过调整来改变弧度
},
letterSpacing: {
type: Number,
default: 0 // 字距
},
startAngle: {
type: Number,
default: 0 // 起始角度
},
ellipseFactor: {
type: Number,
default: 1 // 椭圆因子,1表示圆形,<1表示垂直压缩,>1表示水平拉伸
}
},
mounted() {
this.drawCurvedText();
},
methods: {
drawCurvedText() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');

// 设置字体
ctx.font = `${this.fontSize}px Arial`;
ctx.textBaseline = 'middle';

const text = this.text;
const radius = this.radius;
const canvasWidth = this.canvasWidth;
const canvasHeight = this.canvasHeight;
const textLength = text.length;
const letterSpacing = this.letterSpacing;
const startAngle = this.startAngle;
const ellipseFactor = this.ellipseFactor;

// 中心点
const centerX = canvasWidth / 2;
const centerY = canvasHeight / 2;

// 弧度范围:从 -curvature 到 curvature
const angleRange = Math.PI * this.curvature;

// 每个字符的角度差,考虑字距
const totalSpacing = letterSpacing * (textLength - 1);
const angleStep = (angleRange - totalSpacing / radius) / (textLength - 1);

// 计算起始角度,使得中间的字是正竖着的
const halfTextLength = Math.floor(textLength / 2);

for (let i = 0; i < textLength; i++) {
const char = text[i];
// 当前字符的角度,相对于中间字符的对称分布
const angle = startAngle + (i - halfTextLength) * (angleStep + letterSpacing / radius);

let x = centerX + radius * Math.sin(angle) * ellipseFactor;
let y = centerY - radius * Math.cos(angle);

// 保存当前绘图状态
ctx.save();

// 移动画布到字符位置并旋转
ctx.translate(x, y);
ctx.rotate(angle); // 直接使用当前字符的角度旋转

// 如果是中间的字符,保持正竖着
if (i === halfTextLength) {
ctx.rotate(-angle); // 中间字符保持正竖着
}

// 绘制字符
ctx.fillText(char, 0, 0);

// 恢复之前保存的绘图状态
ctx.restore();
}
}
},
watch: {
text() {
this.drawCurvedText();
},
radius() {
this.drawCurvedText();
},
canvasWidth() {
this.drawCurvedText();
},
canvasHeight() {
this.drawCurvedText();
},
fontSize() {
this.drawCurvedText();
},
curvature() {
this.drawCurvedText();
},
letterSpacing() {
this.drawCurvedText();
},
startAngle() {
this.drawCurvedText();
},
ellipseFactor() {
this.drawCurvedText();
}
}
}
</script>

<style scoped>
canvas {
border: 1px solid #000;
display: block;
margin: 0 auto;
}
</style>