Графика фазы луны в TypeScript неправильно отображает фазы роста и убывания

Я работаю над компонентом React/TypeScript, который отображает фазы луны на основе значения от 0 до 1. Проблема в том, что фазы луны не отображаются правильно между фазами первой четверти и последней четверти. В частности, компонент показывает растущую луну, хотя она должна быть убывающей, и наоборот.

Я попробовал использовать Canvas вместо SVG, но это не решило проблему. Я также попытался изменить расчет видимой части Луны так:

const visiblePortion = Math.cos(normalizedPhase * Math.PI * 2);

Но проблема не исчезла. Вместо того, чтобы правильно отображать луну в диапазоне от 0,25 до 0,75, светлые/темные части луны по-прежнему меняются местами для этих фаз.

Фазы Луны по шкале от 0 до 1

Фаза луны представлена ​​числом от 0 до 1, где:

  • 0: Новолуние (освещенность 0%)
  • 0,125: Растущий полумесяц (освещенность ~25% справа)
  • 0,25: Первая четверть (освещенность 50% справа)
  • 0,375: Растущая луна (освещенность ~75% справа)
  • 0,5: Полнолуние (100% освещенность)
  • 0,625: Убывающая Луна (освещенность ~75% слева)
  • 0,75: Последняя четверть (50% освещенности слева)
  • 0,875: Убывающий полумесяц (освещенность ~25% слева)
  • 1: Новолуние (освещенность 0%)

Я хочу, чтобы луна постепенно увеличивалась между 0 и 0,5, а затем убывала между 0,5 и 1, но для фаз между 0,25 и 0,75 светлые и темные части меняются местами.

Текущий код

Вот код, с которым я работаю:

import React from "react";

interface MoonPhaseProps {
  phase: number;
  moonTexture?: string;
  rotation?: number;
}

const MoonPhase = ({
  phase,
  moonTexture = "/image/moon/moon.svg",
  rotation = 0,
}: MoonPhaseProps) => {
  // Ensure phase is between 0 and 1
  const normalizedPhase = Math.max(0, Math.min(phase, 1));

  console.info(phase, normalizedPhase);

  // Convert phase to radians
  const phaseRadians = normalizedPhase * 2 * Math.PI;

  // Determine if it's a waxing or waning phase
  const isWaxing = normalizedPhase <= 0.5;

  // Calculate the visible portion of the moon
  const visiblePortion = Math.cos(phaseRadians);

  // Adjust the path for waxing and waning phases
  const pathD = isWaxing
    ? `M50,0 A50,50 0 1,1 50,100 A${
        50 * Math.abs(visiblePortion)
      },50 0 1,0 50,0`
    : `M50,0 A50,50 0 1,0 50,100 A${
        50 * Math.abs(visiblePortion)
      },50 0 1,1 50,0`;

  return (
    <svg
      width = "100%"
      viewBox = "0 0 100 100"
      data-phase = {phase}
      style = {{
        transform: `rotate(${rotation}deg)`,
        transition: "transform 0.3s ease-in-out",
      }}
    >
      <defs>
        <pattern
          id = "moonTexturePattern"
          patternUnits = "userSpaceOnUse"
          width = "100"
          height = "100"
        >
          <image href = {moonTexture} x = "0" y = "0" width = "100" height = "100" />
        </pattern>

        <mask id = "moonMask">
          <rect x = "0" y = "0" width = "100" height = "100" fill = "white" />
          <path d = {pathD} fill = "black" />
        </mask>
      </defs>

      {/* Moon texture */}
      <circle cx = "50" cy = "50" r = "49" fill = "url(#moonTexturePattern)" />

      {/* Shading for the dark side of the moon */}
      <circle
        cx = "50"
        cy = "50"
        r = "49"
        fill = "rgba(0,0,0,0.7)"
        mask = "url(#moonMask)"
      />
    </svg>
  );
};

export default React.memo(MoonPhase);

Вот лист (или хотя бы ссылка на него), показывающий фазы Луны и их значения: Moon_phase_explainer

Заранее спасибо.

У вас есть рабочая ссылка ?

Mike 'Pomax' Kamermans 07.09.2024 02:35
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
1
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы хотите, чтобы видимая часть Луны изменялась от 0 до 100% в соответствии с правилом cos (фазы), но вы не смогли проверить, что ваши конечные точки при преобразовании угла имеют смысл. Кажется, вы хотите:

соз(0) = 1, соз(пи/2) = 0, соз(пи) = -1

Но у вас есть строка, устанавливающая фазовый угол в радианах как умноженный на 2*pi:

 const visiblePortion = Math.cos(normalizedPhase * Math.PI * 2);

Это должно читаться

 const visiblePortion = Math.cos(normalizedPhase * Math.PI);

Где знак видимой части определяет, какую сторону скрывать.

И тогда должно быть либо именно то, что вы хотите, либо наоборот (в этом случае s/cos/sin/).

Спасибо за ответ, но это не работает. Луна теперь освещена наполовину на уровне 0,5; он должен быть наполовину освещен с одной стороны на 0,25, а с другой на 0,75, а не 0,5.

Best Codes 06.09.2024 14:46
Ответ принят как подходящий

Я пропустил путь и «построил» маску из эллипса. Я не знаю, сможете ли вы это использовать. В любом случае, возможно, другие смогут.

document.forms.moon.phase.addEventListener('input', e => {
  let e1 = document.getElementById('e1');
  let r1 = document.getElementById('r1');
  let r2 = document.getElementById('r2');
  let val = e.target.value;

  if (val <= .25) {
    r1.setAttribute('fill', '#444');
    r2.setAttribute('fill', 'white');
    e1.setAttribute('fill', '#444');
    e1.setAttribute('rx', 50 - val * 200);
  } else if (val <= .50) {
    r1.setAttribute('fill', '#444');
    r2.setAttribute('fill', 'white');
    e1.setAttribute('fill', 'white');
    e1.setAttribute('rx', (val-.25) * 200);
  } else if (val <= .75) {
    r1.setAttribute('fill', 'white');
    r2.setAttribute('fill', '#444');
    e1.setAttribute('fill', 'white');
    e1.setAttribute('rx', 50 - (val-.50) * 200);
  } else if (val <= 1) {
    r1.setAttribute('fill', 'white');
    r2.setAttribute('fill', '#444');
    e1.setAttribute('fill', '#444');
    e1.setAttribute('rx', (val-.75) * 200);
  }
});
<form name = "moon">
  <input name = "phase" type = "range" min = "0" max = "1" value = "0" step = ".01">
</form>
<svg width = "200" viewBox = "0 0 100 100">
  <defs>
    <pattern id = "moonTexturePattern" patternUnits = "userSpaceOnUse"
      width = "100" height = "100">
      <image href = "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/FullMoon2010.jpg/253px-FullMoon2010.jpg" x = "-10" y = "-10" width = "120" height = "120" />
    </pattern>
    <mask id = "moonMask">
      <rect id = "r1" x = "0" y = "0" width = "50" height = "100" fill = "#444" />
      <rect id = "r2" x = "50" y = "0" width = "50" height = "100" fill = "#444" />
      <ellipse id = "e1" cx = "50" cy = "50" rx = "0" ry = "50" fill = "black"/>
    </mask>
  </defs>
  <circle cx = "50" cy = "50" r = "49" fill = "url(#moonTexturePattern)"
    mask = "url(#moonMask)" />
</svg>
<p><a href = "https://en.wikipedia.org/wiki/File:FullMoon2010.jpg">Moon image from wikimedia.org</a></p>

Все в порядке, но я сделал маску, потому что по умолчанию фоном должна быть луна. В вашем примере часть луны не видна (например на 0.25 левая половина - это ничего, а правая половина - это луна. Я хочу, чтобы было там, где левая половина - это луна, а правая половина — это затемненная луна.) Другого способа сделать это без маски я не нашла.

Best Codes 06.09.2024 20:43

@BestCodes Я обновил свой ответ: заменил все черные заливки в маске серым цветом. Это больше похоже на это?

chrwahl 06.09.2024 21:46

Большое спасибо за все ваши усилия! Это намного лучше, хотя и не идеально. Вместо светлого наложения у меня темное. Вот как выглядит моя луна на 0,2 (на данный момент она все еще работает правильно): i.imgur.com/z90AGCN.png

Best Codes 06.09.2024 23:48

Я думаю, что могу просто изменить цвет белой области. Спасибо!

Best Codes 07.09.2024 00:49

Итак, похоже, что маска просто меняет прозрачность изображения луны, а это означает, что «маска» будет того же цвета, что и фон, на котором используется луна. Чтобы сделать его черным, нам нужно встроить в SVG черный фон. Просто добавьте еще один круг в SVG перед другим: <circle cx = "50" cy = "50" r = "49" fill = "black" /> Спасибо за вашу помощь!

Best Codes 07.09.2024 01:12

Другие вопросы по теме

Невозможно назначить '', поскольку это свойство доступно только для чтения для статического свойства только для чтения в машинописном тексте
Angular 18 Реактивная форма ОШИБКА: к элементу управления формой с именем: 'q1124' не прикреплен экземпляр FormControl
Проблемы с отложенной обработкой заданий в BullMQ
Десериализовать строку JSON, содержащую несколько массивов пользовательских объектов
Как правильно использовать дженерики с условными типами в TypeScript для обработки нескольких ключей?
Использование `keyof typeof enum` в качестве параметра функции
Расширение не загружается в коде Visual Studio — сообщения об отладке не отображаются
GetQueryData всегда возвращает неопределенное значение, даже если установлено с помощью useMutation — TanStack Query, React
Использование querySelector в Typescript: почему он принимает возвращаемый тип HTMLElement?
Есть ли способ отключить утверждения скриншотов в драматурге?