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 // Controls the curvature radius of the text
},
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 degree, can be adjusted to change the arc
},
letterSpacing: {
type: Number,
default: 0 // Letter spacing
},
startAngle: {
type: Number,
default: 0 // Starting angle
},
ellipseFactor: {
type: Number,
default: 1 // Ellipse factor, 1 for circle, <1 for vertical compression, >1 for horizontal stretching
}
},
mounted() {
this.drawCurvedText();
},
methods: {
drawCurvedText() {
const canvas = this.$refs.canvas;
const ctx = canvas.getContext('2d');

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

// Angle range: from -curvature to curvature
const angleRange = Math.PI * this.curvature;

// Angle difference for each character, considering letter spacing
const totalSpacing = letterSpacing * (textLength - 1);
const angleStep = (angleRange - totalSpacing / radius) / (textLength - 1);

// Calculate starting angle to keep the middle character upright
const halfTextLength = Math.floor(textLength / 2);

for (let i = 0; i < textLength; i++) {
const char = text[i];
// Current character's angle, symmetrically distributed relative to the middle character
const angle = startAngle + (i - halfTextLength) * (angleStep + letterSpacing / radius);

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

// Save the current drawing state
ctx.save();

// Move the canvas to the character position and rotate
ctx.translate(x, y);
ctx.rotate(angle); // Directly use the current character's angle for rotation

// If it is the middle character, keep it upright
if (i === halfTextLength) {
ctx.rotate(-angle); // Keep the middle character upright
}

// Draw the character
ctx.fillText(char, 0, 0);

// Restore the previously saved drawing state
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>