更改画布上的字体大小

3

我使用Roko C. Buljan设计的幸运轮,链接在这里:如何绘制一个幸运轮?

这是我第一次使用JS和canvas。 我想把长句子放在标签里,但它们超出了边界。

我尝试使用可伸缩字体,但那并不起作用,也不完全符合我的需求。我需要每个块内的文本移到新的一行,并在超出边界时减小字体。

这段代码可以实现吗,还是我需要从头开始写?

const sectors = [
  {color:"#f82", label:"parallellogram into parallellogram"},
  {color:"#0bf", label:"10"},
  {color:"#fb0", label:"StackStack StackStack"},
  {color:"#0fb", label:"50"},
  {color:"#b0f", label:"StackStackStackStackStackStack"},
  {color:"#f0b", label:"Stack Stack"},
  {color:"#bf0", label:"Stack"},
];

const rand = (m, M) => Math.random() * (M - m) + m;
const tot = sectors.length;
const EL_spin = document.querySelector("#spin");
const ctx = document.querySelector("#wheel").getContext('2d');
const dia = ctx.canvas.width;
const rad = dia / 2;
const PI = Math.PI;
const TAU = 2 * PI;
const arc = TAU / sectors.length;

const friction = 0.991; // 0.995=soft, 0.99=mid, 0.98=hard
let angVel = 0; // Angular velocity
let ang = 0; // Angle in radians

const getIndex = () => Math.floor(tot - ang / TAU * tot) % tot;

function drawSector(sector, i) {
  const ang = arc * i;
  ctx.save();
  // COLOR
  ctx.beginPath();
  ctx.fillStyle = sector.color;
  ctx.moveTo(rad, rad);
  ctx.arc(rad, rad, rad, ang, ang + arc);
  ctx.lineTo(rad, rad);
  ctx.fill();
  // TEXT
  ctx.translate(rad, rad);
  ctx.rotate(ang + arc / 2);
  ctx.textAlign = "right";
  ctx.fillStyle = "#fff";
  ctx.font = "bold 14px sans-serif";
  ctx.fillText(sector.label, rad - 10, 10);
  //
  ctx.restore();
};

function rotate() {
  const sector = sectors[getIndex()];
  ctx.canvas.style.transform = `rotate(${ang - PI / 2}rad)`;
  EL_spin.textContent = !angVel ? "SPIN" : sector.label;
  EL_spin.style.background = sector.color;
}

function finishedSpinning() { // Called when the wheel stops spinning
  const sector = sectors[getIndex()];
  alert(sector.label);
}

function frame() {
  if (!angVel) return;
  const isSpinning = angVel > 0; // Check if the wheel is currently spinning
  angVel *= friction; // Decrement velocity by friction
  if (angVel < 0.002) angVel = 0; // Bring to stop
  ang += angVel; // Update angle
  ang %= TAU; // Normalize angle
  rotate();
  
  if (isSpinning && angVel === 0) { // If the wheel was spinning, but isn't anymore, it has just stopped
    finishedSpinning();
  }
}

function engine() {
  frame();
  requestAnimationFrame(engine)
}

// INIT
sectors.forEach(drawSector);
rotate(); // Initial rotation
engine(); // Start engine
EL_spin.addEventListener("click", () => {
  if (!angVel) angVel = rand(0.25, 0.35);
});
    #wheelOfFortune {
  display: inline-block;
  position: relative;
  overflow: hidden;
}

#wheel {
  display: block;
}

#spin {
  font: 1.5em/0 sans-serif;
  user-select: none;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  height: 30%;
  margin: -15%;
  background: #fff;
  color: #fff;
  box-shadow: 0 0 0 8px currentColor, 0 0px 15px 5px rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  transition: 0.8s;
}

#spin::after {
  content: "";
  position: absolute;
  top: -17px;
  border: 10px solid transparent;
  border-bottom-color: currentColor;
  border-top: none;
}
<div id="wheelOfFortune">
  <canvas id="wheel" width="300" height="300"></canvas>
  <div id="spin">SPIN</div>
</div>

这就是我正在尝试做的事情 在此输入图片描述

解决方案:HTML5画布ctx.fillText无法换行?

新问题 - 第一个标签中的长单词


在画布内使文本 自动换行 需要大量代码才能实现。最简单的方法是根据弧长与字符长度调整大小。是的,可以在代码中实现它。绘制扇形文本 drawSector() 的函数需要进行调整以获取正确的字体大小。您需要将变量传递到模板文字中,而不是硬编码的 14px - Roko C. Buljan
@RokoC.Buljan 感谢您的回复。我喜欢您的代码,但显然我将不得不使用div和span重新制作它。 - cheech
是的,使用HTML和CSS的rotate:Adeg;text-align:right;处理旋转,测试包装和扇区内对齐会更容易一些。请记住,即使您使用HTML元素进行默认包装,您仍将在某个时候超出空间,并且还需要根据可用空间调整文本大小。等宽字体很容易计算,但否则这仍然是一项困难的任务。还要记住,文字和旋转不会很好地融合。当轮子停下来时,您会看到浏览器调整文本反锯齿。结果相当丑陋。 - Roko C. Buljan
1
是的,我想要计算一个字体大小并将其应用于每个部门的标签,我在问题中附上了我想要做的事情的图片。 - cheech
2
我想让大家知道,我可以整天点击这个按钮——对我最后的两个脑细胞来说是无尽的娱乐。 - Shmack
显示剩余9条评论
1个回答

1

这是我得到的,准备接受任何改进建议(主要是文本居中部分-第89行)

为了突出与明亮颜色背景的对比,给文本添加阴影也是不错的选择。

新问题-如第一个标签中的长单词。

  const sectors = [
  { color: "#0fb", label: "Параллелограмм в паралеллограмме" },
  { color: "#0bf", label: "Бесплатная настройка виджета" },
  { color: "#fb0", label: "Два пресета цветов по цене одного" },
  { color: "#0fb", label: "1строчка" },
  { color: "#b0f", label: "Год премиум поддержки в подарок в подарок" },
  { color: "#f0b", label: "Скидка 10% на любой пакет" },
  { color: "#bf0", label: "Виджет 'Juice Contact' в подарок" },
  { color: "#f82", label: "Скидка 5% на любой пакет" },
  { color: "#bf0", label: "" },
];

function printAtWordWrap(context, text, x, y, lineHeight, fitWidth) {
  fitWidth = fitWidth || 0;

  if (fitWidth <= 0) {
    context.fillText(text, x, y);
    return;
  }
  let words = text.split(" ");
  let currentLine = 0;
  let idx = 1;
  while (words.length > 0 && idx <= words.length) {
    const str = words.slice(0, idx).join(" ");
    const w = context.measureText(str).width;
    if (w > fitWidth) {
      if (idx == 1) {
        idx = 2;
      }
      context.fillText(
        words.slice(0, idx - 1).join(" "),
        x,
        y + lineHeight * currentLine
      );
      currentLine++;
      words = words.splice(idx - 1);
      idx = 1;
    } else {
      idx++;
    }
  }
  if (idx > 0)
    context.fillText(words.join(" "), x, y + lineHeight * currentLine);
}

const rand = (m, M) => Math.random() * (M - m) + m;
const tot = sectors.length;
const EL_spin = document.querySelector("#spin");
const ctx = document.querySelector("#wheel").getContext("2d");
const dia = ctx.canvas.width;
const rad = dia / 2;
const PI = Math.PI;
const TAU = 2 * PI;
const arc = TAU / sectors.length;

const friction = 0.991; // 0.995=soft, 0.99=mid, 0.98=hard
let angVel = 0; // Angular velocity
let ang = 0; // Angle in radians

const getIndex = () => Math.floor(tot - (ang / TAU) * tot) % tot;

// calcFontSize not used
const calcFontSize = () => {
  const maxChars = Math.max(...sectors.map((ob) => ob.label.length));
  const width = rad * 0.9 - 15;
  const w = (width / maxChars) * 1.3;
  const h = ((TAU * rad) / tot) * 0.5;
  return Math.min(w, h);
};

function drawSector(sector, i) {
  const ang = arc * i;
  ctx.save();
  // COLOR
  ctx.beginPath();
  ctx.fillStyle = sector.color;
  ctx.moveTo(rad, rad);
  ctx.arc(rad, rad, rad, ang, ang + arc);
  ctx.lineTo(rad, rad);
  ctx.fill();
  // TEXT
  ctx.translate(rad, rad);
  ctx.rotate(ang + arc / 2);
  ctx.textAlign = "center";
  ctx.fillStyle = "#fff";

  const fontSize = 15;
  ctx.font = `bold ${fontSize}px sans-serif`;

  // values for centering text - not ideal for now (need tuning)
  const w = ctx.measureText(sector.label).width;
  console.log(sector.label, w);
  let y;
  const lineWidth = 130; // width before line break

  switch (true) {
    case w < lineWidth:
      y = fontSize / 3;
      break;
    case w >= lineWidth && w < 2 * lineWidth:
      y = fontSize / 3 - 10;
      break;
    case w >= 2 * lineWidth && w < 3 * lineWidth:
      y = fontSize / 3 - 20;
      break;
    case w >= 3 * lineWidth && w < 4 * lineWidth:
      y = fontSize / 3 - 30;
      break;
    case w >= 4 * lineWidth:
      y = fontSize / 3 - 40;
      break;
    default:
      y = fontSize / 3;
  }
  printAtWordWrap(ctx, sector.label, rad * 0.7, y, 21, lineWidth);

  ctx.restore();
}

function rotate() {
  const sector = sectors[getIndex()];
  ctx.canvas.style.transform = `rotate(${ang - PI / 2}rad)`;
  EL_spin.textContent = !angVel
    ? "SPIN"
    : ""; /* sector.label instead "" - if u want to display text inside spin element */
  EL_spin.style.background = sector.color;
}

function finishedSpinning() {
  // Called when the wheel stops spinning
  const sector = sectors[getIndex()];
  alert(sector.label);
}

function frame() {
  if (!angVel) return;
  const isSpinning = angVel > 0; // Check if the wheel is currently spinning
  angVel *= friction; // Decrement velocity by friction
  if (angVel < 0.002) angVel = 0; // Bring to stop
  ang += angVel; // Update angle
  ang %= TAU; // Normalize angle
  rotate();

  if (isSpinning && angVel === 0) {
    // If the wheel was spinning, but isn't anymore, it has just stopped
    finishedSpinning();
  }
}

function engine() {
  frame();
  requestAnimationFrame(engine);
}

// INIT
sectors.forEach(drawSector);
rotate(); // Initial rotation
engine(); // Start engine
EL_spin.addEventListener("click", () => {
  if (!angVel) angVel = rand(0.25, 0.35);
});
    #wheelOfFortune {
  display: inline-block;
  position: relative;
  overflow: hidden;
}

#wheel {
  display: block;
}

#spin {
  font: 1.5em/0 sans-serif;
  user-select: none;
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 30%;
  height: 30%;
  margin: -15%;
  background: #fff;
  color: #fff;
  box-shadow: 0 0 0 8px currentColor, 0 0px 15px 5px rgba(0, 0, 0, 0.6);
  border-radius: 50%;
  transition: 0.8s;
}

#spin::after {
  content: "";
  position: absolute;
  top: -17px;
  border: 10px solid transparent;
  border-bottom-color: currentColor;
  border-top: none;
}
 

 <body style="background-color: darkcyan">
    <div id="wheelOfFortune">
      <canvas id="wheel" width="480" height="480"></canvas>
      <div id="spin">SPIN</div>
    </div>
  </body>


不错的实现!我想补充一下,以上代码对于动态数量的扇区是不起作用的。但是我相信可以通过使用sectors.length作为字体大小的乘数因子来轻松调整。无论如何,干得好。 - Roko C. Buljan
另外,一个改进的想法是增加加速度(角速度),就像原始示例演示中一样:https://dev59.com/FZHea4cB1Zd3GeqPnlW_#33850748 - Roko C. Buljan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接