Как я мог изменить изображение, увеличивая и уменьшая масштаб?

#javascript #css #reactjs #typescript

#javascript #css #reactjs #typescript

Вопрос:

У меня есть компонент, похожий на галерею, и важная часть:

        

     <Gallery>
      <Header>
        <img src={galleryIcon} alt='Galley icon' />
        <h1>My Gallery</h1>
      </Header>
      <Images isImage={custom.length > 0}>
        {custom.length > 0 amp;amp;
          custom.map((c, i) => (
            <img
              id={`custom${i}`}
              key={`custom${i}`}
              src={c.img}
              alt='brick'
              onClick={() => (setEdit((prev) => !prev), setActual(c))}
            />
          ))}
        <div className='add'>
          <button onClick={() => setGallery((prev) => !prev)}>
            <img src={add} alt='Add icon' />
          </button>
          <p>Click to add a brick from our collections!</p>
        </div>
      </Images>
    </Gallery>

  

Каждое изображение имеет этот стиль:

 img {
    box-sizing: border-box;
    height: 28vh;
    width: 28vh;
    margin-right: 1.5rem;
    cursor: pointer;
  }
  

Кроме того, как только пользователь вводит новое изображение, я изменяю размер, чтобы не нарушать масштаб, с помощью этой функции:

 export function resizeImage(
  file: File | Blob,
  maxWidth: number,
  maxHeight: number,
  scale = 1
): Promise<Blob> {
  return new Promise<Blob>((fulfill, reject) => {
    const image = new Image();

    image.src = URL.createObjectURL(file);

    image.onload = () => {
      URL.revokeObjectURL(image.src);

      const width = image.width;
      const height = image.height;

      if (width <= maxWidth amp;amp; height <= maxHeight) {
        fulfill(file);
      }

      let newWidth = 0;
      let newHeight = 0;

      if (scale !== 1) {
        newWidth = (width * scale) / maxWidth;
        newHeight = (height * scale) / maxHeight;
      } else if (width > height) {
        newHeight = height * (maxWidth / width);
        newWidth = maxWidth;
      } else {
        newWidth = width * (maxHeight / height);
        newHeight = maxHeight;
      }

      console.log(newWidth, newHeight);

      const canvas = document.createElement('canvas');
      canvas.width = newWidth;
      canvas.height = newHeight;

      const context = canvas.getContext('2d');

      context?.drawImage(image, 0, 0, newWidth, newHeight);

      canvas.toBlob((blob) => blob amp;amp; fulfill(blob), file.type);
    };

    image.onerror = reject;
  });
}
  

И, наконец, компонент изменения размера:

 const Resize: React.FC<Props> = ({ actual, setResize, setCustom, custom }) => {
  let stats: Stats = {} as Stats;

  const getStats = useCallback((): Stats => stats, []);

  const updateStats = useCallback(
    (newStats: Stats): Stats => (stats = newStats),
    []
  );

  const getResizedCustom = useCallback(
    async (copy: Custom): Promise<Custom> => {
      const actualWidth = window.innerWidth;
      const actualHeight = window.innerHeight;

      const maxWidth = actualWidth < 1152 ? 180 : 360;
      const maxHeight = actualHeight < 800 ? 180 : 360;

      const newBlob = await resizeImage(
        copy.blob,
        maxWidth,
        maxHeight,
        getStats().scale
      );

      return {
        blob: newBlob,
        img: URL.createObjectURL(newBlob),
        id: copy.id,
        type: 'custom',
        price: copy.price,
        amount: copy.amount,
      };
    },
    [stats]
  );

  const updateActual = useCallback(async () => {
    // remove actual
    const newCustomArr = [...custom];
    const customCopy = newCustomArr.splice(newCustomArr.indexOf(actual), 1)[0];

    const newCustom = await getResizedCustom(customCopy);

    console.log(customCopy);
    console.log(newCustom);
    setCustom([...newCustomArr, newCustom]);
  }, [actual, custom, setCustom, getResizedCustom]);

  return (
    <Container>
      <Header>
        <h1>ADJUST YOUR BRICK</h1>
        <img
          src={close}
          alt='close icon'
          onClick={() => setResize((prev) => !prev)}
        />
      </Header>
      <Main>
        <h2>Pinch and zoom</h2>
        <TransformWrapper onZoomChange={updateStats}>
          <TransformComponent>
            <img src={actual.img} alt='brick' />
          </TransformComponent>
        </TransformWrapper>
        <button
          onClick={async () => (
            await updateActual(), setResize((prev) => !prev)
          )}
        >
          DONE
        </button>
      </Main>
    </Container>
  );
};
  

Я также использую react-zoom-pan-pinch, чтобы иметь возможность увеличивать и уменьшать масштаб изображения. Мой вопрос: как я мог бы изменить размер изображения в DOM на основе масштаба, предоставляемого функцией onZoomChange из компонента TransformWrapper? Возможно ли это? Есть лучший и менее «хакерский» способ изменить размер изображения в DOM на основе масштаба масштабирования?

Я постараюсь в ближайшее время предоставить минимальное репо, но пока вот полное репо: https://github.com/Mdsp9070/brickart/tree/dev

Ответ №1:

Это, конечно, возможно, но вам нужно передать больше реквизита drawImage . Ознакомьтесь с документами. У него есть еще четыре необязательных аргумента, которые вы не используете. Если вы используете только первые четыре, то включается все изображение. При использовании всех восьми реквизитов первые четыре указывают, какую часть исходного изображения использовать (обрезка), в то время как следующие четыре указывают, куда поместить этот выбор изображения на вашем холсте.

Вам нужно выполнить некоторую математику и отслеживание событий, чтобы вычислить исходный прямоугольник на основе центра масштабирования и масштаба.