旋转元素内文本方向修复

4

我正在尝试使用HTML、CSS和JS构建一个动画时钟,我已经成功地将小时数定位到正确的位置,但是数字现在不是直的。

我一直在研究text-orientation属性,并尝试使用text-orientation:autotext-orientation:mixed,但似乎都没有起作用。

这是目前我的时钟的样子:

const clock = document.getElementById('clock');
const numbers = clock.children;

for (let i = 0; i < 12; i++) {
  numbers[i].style.transform = 'translate(0, -50%) rotate(' + (90 + i * 30) + 'deg)';
}

const hours = document.getElementById('hours');
const minutes = document.getElementById('minutes');
const seconds = document.getElementById('seconds');

setInterval(function() {
  const time = new Date();
  hours.style.transform = 'translate(0, -50%) rotate(' + (time.getHours() % 12 * 30 - 90) + 'deg)';
  minutes.style.transform = 'translate(0, -50%) rotate(' + (time.getMinutes() * 6 - 90) + 'deg)';
  seconds.style.transform = 'translate(0, -50%) rotate(' + (time.getSeconds() * 6 - 90) + 'deg)';

});
#clock {
  width: 200px;
  height: 200px;
  border: 3px solid black;
  border-radius: 50%;
  position: relative;
}

.hour{
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  text-orientation: mixed;
}

.handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: left center;
}

#hours {
  border-bottom: 3px solid black;
  width: 25%;
}

#minutes {
  border-bottom: 2px solid black;
  width: 35%;
}

#seconds {
  border-bottom: 1px solid red;
  width: 45%;
}
<div id="clock">
  <div class="hour">- 12</div>
  <div class="hour">- 1</div>
  <div class="hour">- 2</div>
  <div class="hour">- 3</div>
  <div class="hour">- 4</div>
  <div class="hour">- 5</div>
  <div class="hour">- 6</div>
  <div class="hour">- 7</div>
  <div class="hour">- 8</div>
  <div class="hour">- 9</div>
  <div class="hour">- 10</div>
  <div class="hour">- 11</div>
  <div class="handle" id="hours"></div>
  <div class="handle" id="minutes"></div>
  <div class="handle" id="seconds"></div>
</div>

2个回答

4
您无法使用text-orientation实现此目标。此属性更多地涉及阅读方向而不是设计或布局,并且仅适用于垂直模式下的文本,正如MDN所述:
引用:

The text-orientation CSS property sets the orientation of the text characters in a line. It only affects text in vertical mode (when writing-mode is not horizontal-tb). It is useful for controlling the display of languages that use vertical script, and also for making vertical table headers.

相反,您需要将小时数包裹在单独的divspan中,并为其设置固定的width以获得更一致的外观,然后对其应用相反的transform:rotate(...)
此外,您可能希望逐渐旋转分和时针。也就是说,如果是12:30,则小时指针应该在12和1之间的中点。
并且您应该给setInterval添加一个delay。现在它被调用的频率远远超过更新指针所需的次数(每秒一次)。

const hoursHandle = document.getElementById('hours');
const minutesHandle = document.getElementById('minutes');
const secondsHandle = document.getElementById('seconds');

// Use querySelectorAll and avoid iterating over the handles by mistake:
document.querySelectorAll('.hour').forEach((child, i) => {
  const angle = 90 + i * 30;
  
  child.style.transform = `translate(0, -50%) rotate(${ angle }deg)`;
  child.children[0].style.transform = `rotate(${ -angle }deg)`;
});

function updateHandles() {
  // With requestAnimationFrame we avoid forcing a repaint:
  
  requestAnimationFrame(() => {
    const time = new Date();
    const seconds = time.getSeconds();
    const minutes = time.getMinutes();
    const hours = time.getHours() % 12;
    const secondsAngle = -90 + 6 * seconds;
    const minutesAngle = -90 + 6 * (minutes + seconds/60);
    const hoursAngle = -90 + 30 * (hours + minutes/60 + seconds/3600); 

    hoursHandle.style.transform = `translate(0, -50%) rotate(${ hoursAngle }deg)`;
    minutesHandle.style.transform = `translate(0, -50%) rotate(${ minutesAngle }deg)`;
    secondsHandle.style.transform = `translate(0, -50%) rotate(${ secondsAngle }deg)`;
  });
}

updateHandles();

setInterval(updateHandles, 250);
body {
  font-family: monospace;
  margin: 0;
}

#clock {
  margin: 16px auto;
  width: 256px;
  height: 256px;
  border-radius: 50%;
  position: relative;
  box-shadow: 0 0 64px rgba(0, 0, 0, .125);
}

.hour {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  font-weight: bold;
}

/*
  Add the marks with CSS rather than using a dash:
*/

.hour::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 4px;
  border-top: 1px solid black;
  transform: translate(0, -50%);
}

.hour > div {
  width: 32px;
  padding: 0 8px;
  display: flex;
  justify-content: center;
}

.handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: left center;
}

#hours {
  background: rgba(0, 0, 0, .5);
  height: 6px;
  right: 64px;
}

#minutes {
  background: rgba(0, 0, 0, .25);
  height: 4px;
  right: 48px;
}

#seconds {
  background: red;
  height: 2px;
  right: 16px;
}

#seconds::before,
#seconds::after {
  content: '';
  position: absolute;
  background: red;
}

#seconds::before {
  top: 0;
  left: -24px;
  width: 24px;
  height: 100%;
}

#seconds::after {
  top: 50%;
  left: 0;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%);
  border-radius: 100%;
}
<div id="clock">
  <div class="hour"><div>12</div></div>
  <div class="hour"><div>1</div></div>
  <div class="hour"><div>2</div></div>
  <div class="hour"><div>3</div></div>
  <div class="hour"><div>4</div></div>
  <div class="hour"><div>5</div></div>
  <div class="hour"><div>6</div></div>
  <div class="hour"><div>7</div></div>
  <div class="hour"><div>8</div></div>
  <div class="hour"><div>9</div></div>
  <div class="hour"><div>10</div></div>
  <div class="hour"><div>11</div></div>
  
  <div class="handle" id="hours"></div>
  <div class="handle" id="minutes"></div>
  <div class="handle" id="seconds"></div>
</div>

今天是《守望者》最后一集的发布,仅此而已...

const clock = document.getElementById('clock');
const hoursHandle = document.getElementById('hours');
const minutesHandle = document.getElementById('minutes');
const secondsHandle = document.getElementById('seconds');

// Use querySelectorAll and avoid iterating over the handles by mistake:
document.querySelectorAll('.hour').forEach((child, i) => {
  const angle = 90 + i * 30;
  
  child.style.transform = `translate(0, -50%) rotate(${ angle }deg)`;
});

// Add 60 marks around the block:
for (let i = 0; i < 60; ++i) {
  const mark = document.createElement('DIV');
  
  mark.className = 'mark';
  mark.style.transform = `rotate(${ i * 6 }deg)`;
  
  clock.appendChild(mark);
}

function updateHandles() {
  // With requestAnimationFrame we avoid forcing a repaint:
  
  requestAnimationFrame(() => {
    const time = new Date();
    const seconds = time.getSeconds();
    const minutes = time.getMinutes();
    const hours = time.getHours() % 12;
    const secondsAngle = -90 + 6 * seconds;
    const minutesAngle = -90 + 6 * (minutes + seconds/60);
    const hoursAngle = -90 + 30 * (hours + minutes/60 + seconds/3600); 

    hoursHandle.style.transform = `translate(0, -50%) rotate(${ hoursAngle }deg)`;
    minutesHandle.style.transform = `translate(0, -50%) rotate(${ minutesAngle }deg)`;
    secondsHandle.style.transform = `translate(0, -50%) rotate(${ secondsAngle }deg)`;
  });
}

updateHandles();

setInterval(updateHandles, 250);
body {
  font-family: monospace;
  margin: 0;
  background: black;
}

body::before,
body::after {
  content: '';
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  pointer-events: none;
}

body::before {
  background: linear-gradient(-135deg, rgba(0, 0, 255, .125), transparent);
}

body::after {
  background: linear-gradient(45deg, rgba(255, 0, 0, .125), transparent);
  z-index: 1;
}

#clock {
  margin: 16px auto;
  width: 256px;
  height: 256px;
  border-radius: 50%;
  position: relative;
  background: #fbdf27;
  text-shadow: 0 0 1px black;
}

.hour {
  position: absolute;
  top: 50%;
  left: 0;
  width: 50%;
  font-weight: bold;
  font-size: 10px;
  transform-origin: right center;
  background: #fbdf27;
  z-index: 1;
}

.hour > div {
  width: 16px;
  display: flex;
  justify-content: center;
  transform: rotate(-90deg);
}

.handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: left center;
  background: blue;
  z-index: 100;
  mix-blend-mode: color-burn;
}

#hours {
  height: 6px;
  right: 64px;
}

#minutes {
  height: 4px;
  right: 48px;
}

#seconds {
  height: 2px;
  right: 16px;
}

#seconds::before,
#seconds::after {
  content: '';
  position: absolute;
  background: blue;
}

#seconds::before {
  top: 0;
  left: -24px;
  width: 24px;
  height: 100%;
  mix-blend-mode: color-burn;
}

#seconds::after {
  top: 50%;
  left: 0;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%);
  border-radius: 100%;
}

#title {
  position: absolute;
  top: 192px;
  left: 50%;
  width: 272px;
  color: #fbdf27;
  background: black;
  font-size: 62px;
  line-height: 40px;
  text-align: center;
  transform: translate(-50%, -50%);
  z-index: 50;
}

.mark {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  transform: translate(-50%, 0);
}

.mark::before {
  content: '';
  position: absolute;
  top: 6px;
  right: 0;
  left: 0;
  height: 4px;
  background: black;
}
<div id="title">WATCHMEN</div>

<div id="clock">    
  <div class="hour"><div>XII</div></div>
  <div class="hour"><div>I</div></div>
  <div class="hour"><div>II</div></div>
  <div class="hour"><div>III</div></div>
  <div class="hour"><div>IV</div></div>
  <div class="hour"><div>V</div></div>
  <div class="hour"><div>VI</div></div>
  <div class="hour"><div>VII</div></div>
  <div class="hour"><div>VIII</div></div>
  <div class="hour"><div>IX</div></div>
  <div class="hour"><div>X</div></div>
  <div class="hour"><div>XI</div></div>
  
  <div class="handle" id="hours"></div>
  <div class="handle" id="minutes"></div>
  <div class="handle" id="seconds"></div>
</div>


1
非常感谢您提供的所有额外评论和更改,它们肯定也会有所帮助! - Ros

1
你可以通过仅考虑平移和cos/sin公式来进行不同的计算。
.8将定义距离中心的距离,请根据您的需要进行调整。不要忘记cos/sin采用弧度角,因此我们必须乘以`Math.PI/180`。
我考虑了一个小线条的背景技巧:

const clock = document.getElementById('clock');
const numbers = clock.children;

const h = 0.8 * (clock.offsetHeight/2);

for (let i = 0; i < 12; i++) {
  numbers[i].style.transform = 
    'translate(-50%, -50%) translate(' 
     + (-h*Math.cos((90 + i * 30)*Math.PI/180)) + 'px,' 
     + (-h*Math.sin((90 + i * 30)*Math.PI/180)) + 'px)';
}

const hours = document.getElementById('hours');
const minutes = document.getElementById('minutes');
const seconds = document.getElementById('seconds');

setInterval(function() {
  const time = new Date();
  hours.style.transform   = 'rotate(' + (time.getHours() % 12 * 30 - 90) + 'deg)';
  minutes.style.transform = 'rotate(' + (time.getMinutes() * 6 - 90) + 'deg)';
  seconds.style.transform = 'rotate(' + (time.getSeconds() * 6 - 90) + 'deg)';

});
#clock {
  width: 200px;
  height: 200px;
  border: 3px solid black;
  padding:5px; /* Control the length of the lines*/
  box-sizing:border-box;
  border-radius: 50%;
  position: relative;
  
  background:
    linear-gradient(#fff,#fff) content-box,
    
    linear-gradient(#000,#000) center/100% 2px,
    linear-gradient(#000,#000) center/2px 100%,
    linear-gradient(30deg,
      transparent calc(50% - 1px), 
      #000 calc(50% - 1px) calc(50% + 1px),
      transparent calc(50% + 1px)),
    linear-gradient(-30deg,
      transparent calc(50% - 1px), 
      #000 calc(50% - 1px) calc(50% + 1px),
      transparent calc(50% + 1px)),
    linear-gradient(60deg,
      transparent calc(50% - 1px), 
      #000 calc(50% - 1px) calc(50% + 1px),
      transparent calc(50% + 1px)),
   linear-gradient(-60deg,
      transparent calc(50% - 1px), 
      #000 calc(50% - 1px) calc(50% + 1px),
      transparent calc(50% + 1px))  ;
  background-repeat:no-repeat;
    
}

.hour,
.handle{
  position: absolute;
  top: 50%;
  left: 50%;
  transform-origin: left;
}

#hours {
  border-bottom: 4px solid black;
  margin-top:-2px;
  width: 25%;
}

#minutes {
  border-bottom: 2px solid black;
  margin-top:-1px;
  width: 35%;
}

#seconds {
  border-bottom: 1px solid red;
  width: 45%;
}
<div id="clock">
  <div class="hour"> 12</div>
  <div class="hour"> 1</div>
  <div class="hour"> 2</div>
  <div class="hour"> 3</div>
  <div class="hour"> 4</div>
  <div class="hour"> 5</div>
  <div class="hour"> 6</div>
  <div class="hour"> 7</div>
  <div class="hour"> 8</div>
  <div class="hour"> 9</div>
  <div class="hour"> 10</div>
  <div class="hour"> 11</div>
  <div class="handle" id="hours"></div>
  <div class="handle" id="minutes"></div>
  <div class="handle" id="seconds"></div>
</div>


谢谢您的回答,但这应该适用于不同的尺寸。我只是在这里将其设置为“200px”,以使其适合。 - Ros
2
@Ros,请检查更新,由于您正在使用JS,可以通过动态读取高度值来轻松调整此部分。 - Temani Afif

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