компилируется...
Перейти к основному содержанию
Рекомендовать во Вконтакте Рекомендовать в Фейсбуке Рекомендовать в Твиттере

Изменение размера и управление изображениями с помощью JavaScript напрямую из браузера.

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

Позже вы решили сжать изображения, чтобы сэкономить место в хранилище, поэтому реализовали функцию сжатия изображений на бекенде. Теперь вы сэкономили место на сервере. Не останавливайтесь на достигнутом, потому что вы можете оптимизировать ещё больше, сэкономить больше ресурсов, таких как пропускная способность и циклы процессора. Если у вас есть сервер с ограниченными ресурсами и вам нужно выполнить множество задач, то вы просто добавляете нагрузку на процессор.

Что, если вы сможете сэкономить место на диске, пропускную способность и одновременно снизить нагрузку на сервер. Да, возможно, ответом будет "Сжатие на стороне клиента с помощью JavaScript". Теперь давайте воплотим это в жизнь.

Воспользуйтесь преимуществами canvas HTML5, который используется для рисования графики на веб-странице. Canvas - это всего лишь контейнер для вашей графики, для рисования используется JavaScript.

Инструкция

1. Создайте экземпляр JavaScript FileReader API:

const reader = new FileReader();

2. Прочитайте полученное изображение с помощью FileReader:

reader.readAsDataURL(sourceImage);

3. Создайте экземпляр Image:

const img = new Image();

4. Установите результат работы FileReader в качестве источника изображения:

img.src = event.target.result;

5. Создайте элемент HTML5 Canvas:

const canvas = document.createElement('canvas');

6. Установите ширину и высоту холста в соответствии с новыми размерами изображения:

canvas.width = width;
canvas.height = height;

7. Создайте объект, который используется для рисования графики на холсте:

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

Метод getContext() возвращает объект со свойствами и методами, необходимыми для рисования графики на холсте. Параметр '2d' ограничивает нас для рисования только 2D-графики.

Теперь нарисуйте изображение на холсте, указав положение, ширину и высоту изображения:

ctx.drawImage(img, 0, 0, width, height);

Экспорт холста в виде blob или DataURL, указав MIME тип и качество изображения:

const data = ctx.canvas.toDataURL(img, mime, quality);
ctx.canvas.toBlob((blob) => {
    console.log(blob); //output image as a blob
    const file = new File([blob], fileName, {
        type: mime,
        lastModified: Date.now()
    }); //output image as a file
}, mime, quality);

mime - это "тип mime" изображения, например, 'image/jpeg', 'image/png'.

Значение качества варьируется от 0 до 1 - это качество получаемого изображения. Если вы не укажете mime и качество в методе toBlob(), то будет установлено качество по умолчанию и тип mime будет 'image/png'.

Итоговый код

/*
<!-- HTML Part -->
<input id="file" type="file" accept="image/*">
<script>
  document.getElementById("file").addEventListener("change", function (event) {
  compress(event);
});

</script>
*/

compress(e) {
  const width = 500;
  const height = 300;
  const fileName = e.target.files[0].name;
  const reader = new FileReader();

  reader.readAsDataURL(e.target.files[0]);
  reader.onload = event => {
    const img = new Image();

    img.src = event.target.result;
    img.onload = () => {
      const elem = document.createElement('canvas');
      elem.width = width;
      elem.height = height;
      const ctx = elem.getContext('2d');
      // img.width и img.height будет содержать оригинальные размеры
      ctx.drawImage(img, 0, 0, width, height);
      ctx.canvas.toBlob((blob) => {
        const file = new File([blob], fileName, {
          type: 'image/jpeg',
          lastModified: Date.now()
        });

      }, 'image/jpeg', 1);
    };
    reader.onerror = error => console.log(error);
  };
}

Замечание

Если вы хотите сохранить соотношение сторон выходного изображения, вы можете установить ширину или высоту как постоянные и вычислить другой размер:

const width = 600;
const scaleFactor = width / img.width;
canvas.width = width;
canvas.height = img.height * scaleFactor;

ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);

Здесь ширина остаётся неизменной и вычисляется коэффициент масштабирования . Чтобы найти относительную высоту, просто умножьте коэффициент масштабирования на исходную высоту.

Для браузеров, которые не поддерживают метод "toBlob".

Используйте этот полифил https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob.

Измените параметры toBlob, как показано на рисунке, иначе вы получите ошибку "function expected".

//toBlob polyfill
if (!HTMLCanvasElement.prototype.toBlob) {
  Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
    value: function (callback, type, quality) {
      var dataURL = this.toDataURL(type, quality).split(',')[1];
      setTimeout(function() {
        var binStr = atob( dataURL ),
            len = binStr.length,
            arr = new Uint8Array(len);
        for (var i = 0; i < len; i++ ) {
          arr[i] = binStr.charCodeAt(i);
        }
        callback( new Blob( [arr], {type: type || 'image/png'} ) );
      });
    }
  });
}

// toBlob usage
ctx.canvas.toBlob(function (blob) {
 console.log(blob); //access blob here
 }, mimeType, quality);

Для Angular и RxJS

import { Component } from '@angular/core';
import { map, expand } from 'rxjs/operators';
import { EMPTY } from 'rxjs';
import { CompressorService } from './compressor.service';

@Component({

  selector: 'app-root',

  template: '<input type="file" (change)="process($event)" multiple/>',

  styles: ['']

})

export class AppComponent {

  constructor(private compressor: CompressorService) {}

  data: FileList;

  compressedImages = [];

  recursiveCompress = (image: File, index, array) => {

    return this.compressor.compress(image).pipe (

      map(response => {
        // Код блока после завершения каждого сжатия

        console.log('compressed ' + index + image.name);

        this.compressedImages.push(response);

        return {

          data: response,

          index: index + 1,

          array: array,

        };

      }),

    );

  }

 
  // процесс загрузки файла

  public process (event) {

  this.data = event.target.files;

  console.log('input: '  + this.data);

  const compress = this.recursiveCompress( this.data[0], 0, this.data ).pipe(

    expand(res => {

      return res.index > res.array.length - 1

        ? EMPTY

        : this.recursiveCompress( this.data[res.index], res.index, this.data );

    }),

  );

  compress.subscribe(res => {

    if (res.index > res.array.length - 1) {
      // Блок кода после завершения сжатия всех данных
      console.log('Compression successful ' + this.compressedImages);
    }

  });

}

}
import { Injectable } from '@angular/core';

import { Observable} from 'rxjs';

@Injectable({

  providedIn: 'root'

})

export class CompressorService {

  constructor() { }

  compress(file: File): Observable<any> {

    const width = 600; // Для масштабирования относительно ширины

    const reader = new FileReader();

    reader.readAsDataURL(file);

    return Observable.create(observer => {

      reader.onload = ev => {

        const img = new Image();

        img.src = (ev.target as any).result;

        (img.onload = () => {

          const elem = document.createElement('canvas'); // Use Angular's Renderer2 method

          const scaleFactor = width / img.width;

          elem.width = width;

          elem.height = img.height * scaleFactor;

          const ctx = <CanvasRenderingContext2D>elem.getContext('2d');

          ctx.drawImage(img, 0, 0, width, img.height * scaleFactor);

          ctx.canvas.toBlob(

            blob => {

              observer.next(

                new File([blob], file.name, {

                  type: 'image/jpeg',

                  lastModified: Date.now(),

                }),

              );

            },

            'image/jpeg',

            1,

          );

        }),

        (reader.onerror = error => observer.error(error));

      };
    });
  }
}

Комментарии

Пока что нет комментариев, вы можете быть первым.
Войти или Регистрация , чтобы оставлять комментарии.

Лучшие публикации

Популярные теги

Наш сайт использует куки. Узнайте больше о нашем использовании куки: политика в отношении файлов cookie
Наш сайт существует только благодаря показу онлайн-рекламы нашим посетителям.
Пожалуйста, поддержите нас, отключив блокировку рекламы.