> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# SVG на Canvas: загрузите графику до экспорта PNG

> Загрузите полный SVG через Image.decode, нарисуйте в выбранном размере растра, проверьте CORS и чтение PNG, храните редактируемый исходник отдельно от экспортированных пикселей.

Source: https://perfectvector.com/ru/blog/svg-canvas-image-export

Language: ru
Author: Irene Kim
Published: 2026-10-06T08:00:00+09:00
Updated: 2026-10-06T08:00:00+09:00

Чтобы нарисовать полный SVG на canvas, загрузите его как изображение, дождитесь `image.decode()` и передайте изображение в `drawImage()`. Затем отдельно проверьте экспорт PNG: изображение может появиться на canvas, хотя браузер блокирует чтение или сохранение пикселей.

Этот способ рисует документ SVG как изображение. Он не даёт canvas редактируемые контуры SVG. Если вместо этого нужно восстановить отдельные контуры, используйте [руководство по SVG в Path2D](https://perfectvector.com/ru/blog/canvas-path2d-svg). При любом способе показа сохраняйте исходный SVG.

## Разделите декодирование, рисование и экспорт

`HTMLImageElement` — допустимый вход [canvas drawImage()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage). Целевые ширина и высота управляют нарисованным размером. [Image.decode()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) возвращает promise, который выполняется после декодирования изображения; неудачный запрос или проблема декодирования могут отклонить его.

Успешное декодирование — лишь первая проверка. Изображение другого источника, загруженное без разрешения CORS, всё ещё можно нарисовать, но оно загрязняет canvas. Чтение пикселей и экспорт затем не работают. Это различие объясняет [руководство MDN по изображениям с CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_image).

**Храните исходник, проверяйте пиксели**

![Концептуальная иллюстрация полого ромба и круга как исходных контуров, гладко отрисованной графики и результата с пиксельной сеткой](https://perfectvector.com/blog/svg-canvas-image-export/image-and-png.webp)

*Иллюстрация: исходные контуры и нарисованные пиксели — разные представления. Рамки, стрелки и крупные сетки концептуальны; браузерные результаты ниже получены в независимо выполненном оригинальном тесте.*

## Создайте самодостаточный оригинальный SVG

Сохраните это как `master.svg`. Файл содержит один контур и один круг, без встроенного изображения, шрифта или внешней зависимости. Внутренний ромб и окружающий холст прозрачны в исходнике.

```svg
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="160" viewBox="0 0 240 160">
  <path d="M96 24 L152 80 L96 136 L40 80 Z M96 58 L118 80 L96 102 L74 80 Z" fill="#176b5b" fill-rule="evenodd"/>
  <circle cx="200" cy="80" r="24" fill="#d4a53a"/>
</svg>
```

Мы используем весь файл как изображение, включая обе фигуры и `viewBox`. В отличие от извлечения одного значения `d` контура, это просит браузерное средство отрисовки SVG-изображений нарисовать документ.

Пример намеренно самодостаточен. SVG как изображение имеет ограничения, в том числе для внешних ресурсов и сценариев; см. [руководство MDN по SVG как изображению](https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_as_an_image). Не считайте успешное декодирование этого небольшого файла доказательством, что произвольный SVG загрузит внешние шрифты, стили или связанную графику. Для выбора способа передачи сравните [встроенный SVG с элементом изображения](https://perfectvector.com/ru/blog/inline-svg-vs-img).

## Запустите два локальных источника с разными ответами CORS

Источник включает свой порт. Следующий исключительно локальный сервер отдаёт одинаковые байты SVG через два источника. Во втором `/cors/master.svg` включает `Access-Control-Allow-Origin: *`, а `/plain/master.svg` — нет. Это отделяет разрешение ответа от графики.

Сохраните это как `serve-proof.py` рядом с SVG и HTML ниже. Запустите `python3 serve-proof.py`, затем откройте `http://127.0.0.1:8770/starter.html`. Оба порта должны быть свободны; если меняете их, обновляйте сервер и HTML вместе. Нажмите Enter в терминале для остановки обоих серверов.

```python
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from threading import Thread

root = Path(__file__).parent
class Handler(SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=str(root), **kwargs)
    def do_GET(self):
        if self.path in ['/cors/master.svg', '/plain/master.svg']:
            payload = (root / 'master.svg').read_bytes()
            self.send_response(200)
            self.send_header('Content-Type', 'image/svg+xml')
            self.send_header('Cache-Control', 'no-store')
            if self.path.startswith('/cors/'):
                self.send_header('Access-Control-Allow-Origin', '*')
            self.end_headers()
            self.wfile.write(payload)
        else:
            super().do_GET()

servers = [ThreadingHTTPServer(('127.0.0.1', port), Handler) for port in (8770, 8783)]
for server in servers:
    Thread(target=server.serve_forever, daemon=True).start()
try:
    input('Original SVG proof on 127.0.0.1:8770 and :8783. Enter to stop.\n')
finally:
    for server in servers:
        server.shutdown()
        server.server_close()
```

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

## Декодируйте изображение перед рисованием

Сохраните эту полную страницу как `starter.html`. Каждому случаю выделен собственный canvas, поэтому загрязнённый случай не может загрязнить успешные. Для запросов, которым нужен CORS, задавайте `crossOrigin` до `src`, как описано в [справочнике MDN по crossOrigin](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin).

Растр canvas явно равен `480 × 320`, а размер показа CSS — `240 × 160`. Собственные размеры исходного изображения — `240 × 160`, и `drawImage()` масштабирует его до выбранных размеров растра. Это выбранные размеры, а не измерение отношения пикселей устройства.

```html
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG to canvas export</title>
<style>
  body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
  main { display: grid; grid-template-columns: repeat(2, 280px); gap: 20px; }
  figure { margin: 0; }
  canvas { display: block; width: 240px; height: 160px; background: white; }
  figcaption { margin: 6px 0; }
  pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Decode, paint, then check PNG access</h1>
<main id="cases"></main>
<pre id="report"></pre>
<script>
  function pngBlob(canvas) {
    return new Promise((resolve, reject) => {
      try { canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Empty PNG')), 'image/png'); }
      catch (error) { reject(error); }
    });
  }
  function dataURL(blob) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = () => reject(reader.error);
      reader.readAsDataURL(blob);
    });
  }
  async function render(test) {
    const figure = document.createElement('figure');
    const canvas = document.createElement('canvas');
    canvas.width = 480;
    canvas.height = 320;
    const caption = document.createElement('figcaption');
    figure.append(canvas, caption);
    document.getElementById('cases').append(figure);
    const result = { id: test.id, url: new URL(test.src, location.href).href,
      crossOrigin: test.crossOrigin ?? null, canvasWidth: canvas.width, canvasHeight: canvas.height };
    const image = new Image();
    if (test.crossOrigin) image.crossOrigin = test.crossOrigin;
    image.src = result.url;
    try { await image.decode(); }
    catch (error) {
      result.stage = 'decode';
      result.error = error.name;
      caption.textContent = test.id + ': decode ' + error.name;
      return result;
    }
    result.naturalWidth = image.naturalWidth;
    result.naturalHeight = image.naturalHeight;
    const context = canvas.getContext('2d');
    context.drawImage(image, 0, 0, canvas.width, canvas.height);
    result.stage = 'painted';
    try {
      result.opening = [...context.getImageData(192, 160, 1, 1).data];
      result.fill = [...context.getImageData(192, 80, 1, 1).data];
      result.corner = [...context.getImageData(0, 0, 1, 1).data];
    } catch (error) { result.readbackError = error.name; }
    try {
      const blob = await pngBlob(canvas);
      result.pngType = blob.type;
      result.pngBytes = blob.size;
      result.pngDataURL = await dataURL(blob);
      result.export = 'ok';
      const download = document.createElement('a');
      download.href = result.pngDataURL;
      download.download = test.id + '.png';
      download.textContent = 'Save PNG';
      figure.append(download);
    } catch (error) { result.export = 'failed'; result.exportError = error.name; }
    caption.textContent = test.id + ': ' + result.stage + ', PNG ' + result.export;
    return result;
  }
  async function run() {
    const tests = [
      { id: 'same-origin', src: 'master.svg' },
      { id: 'cors-allowed', src: 'http://127.0.0.1:8783/cors/master.svg', crossOrigin: 'anonymous' },
      { id: 'no-cors-image', src: 'http://127.0.0.1:8783/plain/master.svg' },
      { id: 'cors-denied', src: 'http://127.0.0.1:8783/plain/master.svg', crossOrigin: 'anonymous' },
      { id: 'missing-file', src: 'missing.svg' },
    ];
    const results = [];
    for (const test of tests) results.push(await render(test));
    document.getElementById('report').textContent = JSON.stringify({ pageOrigin: location.origin, results }, (key, value) => key === 'pngDataURL' ? undefined : value, 2);
  }
  run();
</script>
</html>
```

Функция экспорта обрабатывает и выброшенное исключение, и null вместо blob. [Canvas.toBlob()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) поддерживает вывод PNG и может выбросить `SecurityError`, если canvas не является origin-clean. Два успешных случая добавляют ссылку «Сохранить PNG»; видимый отчёт опускает содержимое base64.

## Прочитайте этап ошибки до изменения SVG

Мы выполнили этот точный стартовый пример в одном браузерном окружении Chrome, затем снова открыли оба сохранённых PNG. Наблюдения относятся к оригинальному самодостаточному файлу и этим двум локальным источникам.

| Случай                            | Запрос изображения                | Наблюдаемые рисование/чтение/экспорт                                |
| --------------------------------- | --------------------------------- | ------------------------------------------------------------------- |
| Тот же источник                   | Локальный SVG без атрибута CORS   | Декодирован в 240 × 160; нарисован; чтение и PNG успешны            |
| Другой источник, разрешено        | `anonymous` плюс заголовок ответа | Декодирован; нарисован; чтение и PNG успешны                        |
| Другой источник, без запроса CORS | Нет атрибута и заголовка ответа   | Декодирован и нарисован; чтение и экспорт выбросили `SecurityError` |
| Другой источник, запрещено        | `anonymous`, нет заголовка ответа | Декодирование отклонено с `EncodingError`; рисование не выполнялось |
| Отсутствующий файл                | 404 того же источника             | Декодирование отклонено с `EncodingError`; рисование не выполнялось |

Случай без CORS явно показывал графику. Этот вид не устанавливал разрешения на чтение или экспорт. Случаи запрета CORS и отсутствующего файла завершились раньше, поэтому переписывание геометрии не исправило бы ни один сбой запроса.

Оба экспортированных PNG снова открылись в `480 × 320`. В этом запуске они были побайтово одинаковыми, по 10 509 байт каждый. В координате растра `(192,160)` отверстие было `RGBA (0,0,0,0)`; угол тоже имел нулевой альфа-канал. Бирюзовый образец в `(192,80)` был `(23,107,91,255)`. Эти образцы проверяют отверстие и заливку данного файла, а не каждый краевой пиксель или каждую функцию SVG.

Белые прямоугольники на странице браузера — фоны CSS за canvas. Это не белая краска, добавленная в PNG. Если результату нужен непрозрачный фон, явно нарисуйте его до изображения и снова проверьте экспортированный файл.

## Храните редактируемый мастер отдельно от PNG

Сохранённый PNG содержит пиксели. Он не переносит исходный контур, круг или редактируемое внутреннее очертание. Храните `master.svg` как векторный исходник и выбирайте размеры растра canvas под назначение, которому действительно нужен PNG. Последующее увеличение существующего PNG не восстановит исходную геометрию.

Одно имя файла SVG также не доказывает, что вся графика внутри — векторная геометрия; используйте [руководство по проверке встроенного растра](https://perfectvector.com/ru/blog/svg-contains-raster-image), если файл содержит связанные или встроенные изображения.

Если исходная графика существует только как растр, а нужны редактируемые контуры, [процесс преобразования изображения в вектор PerfectVector](https://perfectvector.com/ru/image-to-vector) может подготовить мастер SVG. Проверьте очертания, цвета и отверстия перед использованием в этой процедуре загрузки. Векторизация не исправляет заголовки CORS и не даёт разрешения на чтение canvas.

## Частые вопросы

**Можно ли передать разметку SVG прямо в drawImage()?**
Нет. Этот пример загружает файл SVG в HTMLImageElement и передаёт декодированное изображение в drawImage(). Строка разметки — не этот объект изображения. Отделяйте загрузку документа от построения данных контуров Path2D.

**Почему картинка появляется, но экспорт PNG не работает?**
Изображение другого источника, загруженное без разрешения CORS, может загрязнить canvas после рисования. В нашем случае без CORS рисование удалось, но чтение пикселей и экспорт выбросили SecurityError. Проверяйте режим запроса и ответ сервера, а не предполагайте поломку геометрии SVG.

**Исправляет ли crossOrigin anonymous любой URL изображения?**
Нет. Задавайте его до src, а сервер изображения должен дать совместимый ответ CORS. Та же наша графика не декодировалась, когда anonymous запрашивался у адреса без разрешающего заголовка.

**Сохраняет ли экспорт canvas в PNG редактируемые контуры SVG?**
Нет. PNG — растровый результат. Храните исходный SVG отдельно, если нужно редактировать контуры, фигуры или отверстия, и проверяйте PNG в целевом размере передачи.

## Источники

1. [MDN: CanvasRenderingContext2D.drawImage()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage) — Принимаемые объекты изображений, целевые и собственные размеры.
2. [MDN: HTMLImageElement.decode()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) — Promise готовности изображения и отклонение запроса/декодирования.
3. [MDN: Изображения с CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_image) — Ограничения origin-clean на чтение и экспорт нарисованных изображений.
4. [MDN: HTMLImageElement.crossOrigin](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin) — Режим запроса CORS при загрузке изображения.
5. [MDN: HTMLCanvasElement.toBlob()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) — Создание blob PNG, null в результате и SecurityError.
6. [MDN: SVG как изображение](https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_as_an_image) — Контексты SVG-изображений и ограничения внешних ресурсов.

***

Работаете с растровым оригиналом, которому нужна редактируемая графика? [Создайте мастер SVG с PerfectVector](https://perfectvector.com/ru/image-to-vector), проверьте очертание и отверстия, затем сохраняйте мастер, отдельно проверяя отрисовку canvas и экспортированный PNG.

## Explore

- [Read the HTML article](https://perfectvector.com/ru/blog/svg-canvas-image-export)
- [Значки SVG MapLibre: пиксели и размер символа](https://perfectvector.com/ru/blog/maplibre-svg-symbol-image.md)
- [SVG в p5.js: загрузка, масштаб и сохранение исходника](https://perfectvector.com/ru/blog/p5js-svg-artwork.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
