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 // Radius controlling curvature
},
canvasWidth: {
type: Number,
default: 500 // Canvas width
},
canvasHeight: {
type: Number,
default: 600 // Canvas height
},
fontSize: {
type: Number,
default: 32 // Font size
},
curvature: {
type: Number,
default: 0.5 // Curvature factor controls arc span
},
letterSpacing: {
type: Number,
default: 0 // Letter spacing
},
startAngle: {
type: Number,
default: 0 // Starting angle
},
ellipseFactor: {
type: Number,
default: 1 // 1 = circle, <1 vertical squeeze, >1 horizontal stretch
}
},
mounted() {
this.drawCurvedText();
},
methods: {
drawCurvedText() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');

// Set font
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;

// Center point
const centerX = canvasWidth / 2;
const centerY = canvasHeight / 2;

// Arc range derived from curvature
const angleRange = Math.PI * this.curvature;

// Angle step per character (account for spacing)
const totalSpacing = letterSpacing * (textLength - 1);
const angleStep = (angleRange - totalSpacing / radius) / (textLength - 1);

// Middle index for orientation
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); // Keep middle character upright
}

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>