
【JavaScript入門】ファイルのローカルへの保存(controlArea.js)
controlArea.js
は エフェクトボタン群 と ローカル保存/削除機能 を束ねるモジュールです。ユーザーはワンクリックで
- セピア・グレースケール・ぼかし といった加工を即時適用
- 元に戻す/画像 + コメントを PC にダウンロード/作業内容を一掃
という操作を行えます。保存処理では Blob
+ URL.createObjectURL()
を用い、ブラウザ内だけで安全にファイルを書き出します。

フォルダ構成(該当部分)
node-js/
└─ app/
├─ index.html
├─ style.css
└─ js/
├─ common.js
├─ storage.js
├─ imageArea.js
├─ imageEffects.js
├─ controlArea.js ← ★今回実装
├─ commentArea.js
├─ sendArea.js
├─ dateFormat.js
└─ postListArea.js
controlArea.js ― フルコード & 詳細コメント
/* ---------------------------------------------------
controlArea.js
- エフェクトボタン & ファイル操作
- セピア / グレースケール / ぼかし / 元に戻す / 保存 / 削除
--------------------------------------------------- */
window.addEventListener('DOMContentLoaded', () => {
/* ========== DOM 取得 ========== */
const canvas = document.querySelector('#view');
const ctx = canvas.getContext('2d');
// ボタン群
const btnSepia = document.querySelector('#efSepia');
const btnGray = document.querySelector('#efGray');
const btnBlur = document.querySelector('#efBlur');
const btnBack = document.querySelector('#efBack');
const btnSave = document.querySelector('#efSave');
const btnDel = document.querySelector('#efDel');
/* ========== 画像キャッシュ (元に戻す用) ========== */
let cacheData = null; // ImageData の退避先
let cacheTime = null; // Canvas 描画時刻
// Canvas が更新されたら ImageData を確保
function ensureCache(){
const t = canvas.getAttribute('time');
if (t && t !== cacheTime){
cacheData = ctx.getImageData(0,0,canvas.width,canvas.height);
cacheTime = t;
}
}
const hasImage = () => canvas.getAttribute('time') !== null;
/* ========== ボタンイベント ========== */
// ▶ セピア
btnSepia.addEventListener('click', () => {
if(!hasImage()) return;
ensureCache();
applySepia();
});
// ▶ グレースケール
btnGray.addEventListener('click', () => {
if(!hasImage()) return;
ensureCache();
applyCanvasFilter(canvas, ctx, 'grayscale(100%)'); // imageEffects.js で定義
});
// ▶ ぼかし
btnBlur.addEventListener('click', () => {
if(!hasImage()) return;
ensureCache();
applyCanvasFilter(canvas, ctx, 'blur(4px)');
});
// ▶ 元に戻す
btnBack.addEventListener('click', () => {
if (cacheData) ctx.putImageData(cacheData, 0, 0);
});
// ▶ 保存(PNG + コメント TXT)
btnSave.addEventListener('click', () => {
if(!hasImage()) return;
saveImagePNG();
saveCommentTXT();
});
// ▶ 削除(表示・ローカル保存データを完全リセット)
btnDel.addEventListener('click', () => {
// 表示を初期ガイドに戻す
document.querySelector('#noView').style.display = 'flex';
canvas.style.display = 'none';
// コメントクリア
document.querySelector('#comment').value = '';
// localStorage もクリア(storage.js)
removeCanvasStorage();
removeCommentStorage();
restoreCommentFromStorage(); // デフォルト値に復帰
});
/* ========== 内部ユーティリティ ========== */
// ▼ セピア変換:RGB→輝度→色調整の手書き演算
function applySepia(){
const img = ctx.getImageData(0,0,canvas.width,canvas.height);
const d = img.data;
for (let i = 0; i < d.length; i += 4){
const y = 0.299*d[i] + 0.587*d[i+1] + 0.114*d[i+2]; // 輝度
d[i] = y * 240 / 255 | 0; // R
d[i+1] = y * 200 / 255 | 0; // G
d[i+2] = y * 145 / 255 | 0; // B
}
ctx.putImageData(img, 0, 0);
}
// ▼ PNG をダウンロード
function saveImagePNG(){
const url = canvas.toDataURL('image/png'); // Base64 PNG
triggerDownload(url, 'image.png');
}
// ▼ コメントを TXT でダウンロード
function saveCommentTXT(){
const txt = document.querySelector('#comment').value;
const blob = new Blob([txt], {type: 'text/plain'}); // MIME は text/plain
const url = URL.createObjectURL(blob); // Blob → 一時 URL
triggerDownload(url, 'comment.txt');
URL.revokeObjectURL(url); // メモリを即解放
}
// ▼ a.download を仮生成して自動クリック
function triggerDownload(href, filename){
const a = document.createElement('a');
a.href = href;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
}
});
主要 API・命令一覧
命令/プロパティ | 働き | 主な使い方 |
---|---|---|
canvas.getContext('2d') | 描画用の 2D コンテキスト取得 | 画像加工に必須 |
ctx.getImageData() / putImageData() | ピクセル操作 | セピア演算・元に戻す |
applyCanvasFilter() | CSS Canvas フィルター適用 | imageEffects.js で定義 |
canvas.toDataURL('image/png') | Canvas → Base64 PNG | ダウンロード用 URL 生成 |
new Blob([data], {type}) | 任意データをバイト列にラップ | コメント TXT 作成 |
URL.createObjectURL(blob) | Blob → 一時 URL | 大容量でも高速生成 |
URL.revokeObjectURL(url) | 一時 URL を解放 | メモリリーク防止 |
<a>.download | リンク先をファイル保存扱い | 自動 DL トリガーに使用 |
関数別の詳細解説
関数 | 処理内容 |
---|---|
ensureCache() | Canvas の time 属性で変更有無を検知し、更新時のみ ImageData を保存。無駄なメモリコピーを防ぐ。 |
applySepia() | 各ピクセルを輝度 (Y ) に変換して R/G/B を調整、セピア色を作る低レベル演算例。 |
applyCanvasFilter() | CSS の filter を使って高速に加工(外部モジュール)。セピアのみピクセル計算にしたのはデモ目的。 |
triggerDownload(href, fn) | DOM に <a download> を一瞬だけ挿入 → click() → 削除、でユーザー操作無しに保存ダイアログを出す。 |
ポイント & ベストプラクティス
- Blob + createObjectURL
Base64 文字列より高速・省メモリ。作った URL はrevokeObjectURL()
で確実に解放。 - Canvas に
time
属性を持たせる
画像が描画されるたびにDate.now()
をセットし、キャッシュと比較することで “元に戻す” 用のスナップショットを 1 度だけ取得できる。 - エフェクトは 2 系統
低コストな CSS フィルター と ピクセル演算 の両方を示し、用途に応じて選択できる構成に。 - 削除ボタンは “完全リセット”
UI 初期化・コメントクリア・localStorage 削除をワンストップで行い UX 向上。
次回は フォーム送信をページ遷移なしで実現する sendArea.js
を掘り下げ、Ajax で画像 + コメントをサーバへ送り、postListArea.js
へ即時反映するリアルタイム投稿フローを実装します。