فكرة خاصة بي -الأزرار-شرح عملي لبناء محرر صور تفاعلي بلغة JavaScript: الأزرار، الوظائف، والأسرار
- الحصول على الرابط
- X
- بريد إلكتروني
- التطبيقات الأخرى
الخطة الأولى: كتابة الأكواد لكل وظيفة
رح نكتب الأكواد الأساسية لكل زر ذكرتهم، باستخدام:
HTML للأزرار والكانفس
JavaScript لتنفيذ الوظائف (فتح، نسخ، لصق، قص، تعديل، إلخ)
بنية مرنة تسمح بالتوسيع مستقبلاً (مثلاً بإضافة الذكاء الاصطناعي أو الفلاتر)
كل وظيفة راح أشرح الكود خطوة بخطوة، مع الأخطاء الشائعة وطريقة التصحيح.
🚀 الخطة الثانية: بناء تطبيق تحرير صور احترافي
نبدأ من الصفر ونقسم المشروع إلى مراحل:
واجهة المستخدم (UI): تصميم أنيق باستخدام HTML/CSS
كانفس الرسم: ضبط الرسم الحر، الأدوات، والألوان
وظائف التحرير الأساسية: مسح، قص، تعديل، حفظ
إضافة ذكاء صناعي: إصلاح تلقائي، تحديد ذكي، تحسين الصورة
إدارة الحالة: تراجع، إعادة، حفظ مؤقت، تحميل ملف
واجهة متعددة اللغات: دعم العربية وواجهة تفاعلية
تحسين الأداء: ضغط الصور، عرض الرسومات بسلاسة
قلّي إيش يناسبك أكثر؟ أو لو تحب نبدأ مباشرة بكتابة الأكواد لوظيفة معينة (مثلاً “فتح صورة” أو “رسم حر”)، أنا جاهز أكتبها لك الآن 💻🎯
خلينا نكتب الأكواد الأساسية لوظائف التحرير في الكانفس باستخدام JavaScript وHTML. كل وظيفة راح أشرحها بشكل مبسط مع مثال عملي:
📂 1. فتح صورة
<input type="file" id="fileInput" accept="image/*" style="display:none" />
<button onclick="document.getElementById('fileInput').click()">فتح صورة</button>
<canvas id="paint" width="800" height="500"></canvas>
const fileInput = document.getElementById('fileInput');
const canvas = document.getElementById('paint');
const ctx = canvas.getContext('2d');
fileInput.addEventListener('change', function() {
const file = this.files[0];
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
📋 2. نسخ الكانفس
function copyCanvas() {
canvas.toBlob(function(blob) {
const item = new ClipboardItem({ "image/png": blob });
navigator.clipboard.write([item]);
});
}
📥 3. لصق صورة من الحافظة
async function pasteCanvas() {
const items = await navigator.clipboard.read();
for (const item of items) {
if (item.types.includes('image/png')) {
const blob = await item.getType('image/png');
const img = new Image();
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
img.src = URL.createObjectURL(blob);
}
}
}
🧹 4. حذف الكانفس
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
✂️ 5. قص جزء من الكانفس
قص يحتاج منطقة تحديد، ولكن كبداية:
function cropArea(x, y, width, height) {
const imageData = ctx.getImageData(x, y, width, height);
canvas.width = width;
canvas.height = height;
ctx.putImageData(imageData, 0, 0);
}
📝 6. تعديل المحتوى (نص كمثال)
function editContent() {
const text = prompt("أدخل النص");
if (text) {
ctx.font = "20px Arial";
ctx.fillStyle = "black";
ctx.fillText(text, 50, 50);
}
}
رائع! خلينا نكمل كتابة الوظائف المهمة للمحرر 👨💻✨ سأضيف لك الآن الأكواد الخاصة بـ:
الرسم الحر (Free Draw)
حفظ الصورة
استرجاع وتكرار الخطوات (Undo / Redo)
✏️ 1. الرسم الحر
let drawing = false;
canvas.addEventListener("mousedown", () => drawing = true);
canvas.addEventListener("mouseup", () => drawing = false);
canvas.addEventListener("mouseout", () => drawing = false);
canvas.addEventListener("mousemove", draw);
function draw(e) {
if (!drawing) return;
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
ctx.fillStyle = "black"; // لون القلم
ctx.beginPath();
ctx.arc(x, y, 2, 0, Math.PI * 2);
ctx.fill();
}
🔧 يمكنك تخصيص لون وحجم القلم بإضافة متغيرات للواجهة.
💾 2. حفظ الصورة
function saveImage() {
const link = document.createElement("a");
link.download = "canvas-image.png";
link.href = canvas.toDataURL("image/png");
link.click();
}
📥 يحفظ الصورة من الكانفس بصيغة PNG على جهاز المستخدم.
↩️↪️ 3. استرجاع وتكرار الخطوات (Undo / Redo)
let history = [];
let redoHistory = [];
function saveState() {
history.push(canvas.toDataURL());
if (history.length > 50) history.shift(); // تقليل الذاكرة
}
function undo() {
if (history.length === 0) return;
redoHistory.push(canvas.toDataURL());
const img = new Image();
img.src = history.pop();
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
function redo() {
if (redoHistory.length === 0) return;
history.push(canvas.toDataURL());
const img = new Image();
img.src = redoHistory.pop();
img.onload = () => ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
🧠 يفضل استدعاء saveState()
بعد كل عملية رسم أو تعديل لتسجيل الحالة.
هل تحب أضيف أدوات متقدمة مثل تغيير حجم الفرشاة، اختيار الألوان، أو إضافة تأثيرات على الصور؟ أو نبني معاً واجهة مخصصة لذلك؟ خليني أساعدك في تطويره ✨🎨
جميل جدًا! لننتقل إلى أدوات التحديد داخل الكانفس، وهي تنقسم عادة إلى نوعين مهمين:
✏️ 1. التحديد الحر (Free Selection)
يسمح للمستخدم برسم شكل غير منتظم لتحديد جزء من الصورة.
let path = [];
canvas.addEventListener("mousedown", () => path = []);
canvas.addEventListener("mousemove", (e) => {
if (path.length >= 0) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
path.push({x, y});
drawPath(); // ترسم المسار على الكانفس
}
});
canvas.addEventListener("mouseup", () => {
// هنا يمكنك استخدام path لقص أو تعديل الجزء المحدد
});
function drawPath() {
ctx.strokeStyle = "red";
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
for (let i = 1; i < path.length; i++) {
ctx.lineTo(path[i].x, path[i].y);
}
ctx.stroke();
}
💡 يمكنك إغلاق المسار تلقائيًا، أو تعبئته، أو استخدامه للقص.
🖋️ 2. تحديد النقاط (Polygonal Selection)
يسمح بتحديد شكل هندسي باستخدام النقر على نقاط متتالية.
let polygonPoints = [];
let selecting = false;
canvas.addEventListener("click", (e) => {
if (!selecting) {
polygonPoints = [];
selecting = true;
}
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
polygonPoints.push({x, y});
drawPolygon();
});
function drawPolygon() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // امسح التحديد السابق
ctx.strokeStyle = "blue";
ctx.beginPath();
if (polygonPoints.length > 0) {
ctx.moveTo(polygonPoints[0].x, polygonPoints[0].y);
for (let i = 1; i < polygonPoints.length; i++) {
ctx.lineTo(polygonPoints[i].x, polygonPoints[i].y);
}
ctx.stroke();
}
}
// لإنهاء التحديد
document.addEventListener("keydown", (e) => {
if (e.key === "Enter" && polygonPoints.length > 2) {
ctx.lineTo(polygonPoints[0].x, polygonPoints[0].y); // إغلاق الشكل
ctx.stroke();
selecting = false;
// نفذ العملية مثل القص هنا
}
});
سأشرح لك الآن خطوتين احترافيتين:
✂️ 1. ميزة القص داخل التحديد
نفترض أن المستخدم رسم تحديد حر أو نقاط، ونريد قص هذه المنطقة فقط.
function cropPath(path) {
// إنشاء مسار جديد للتحديد
const tempCanvas = document.createElement("canvas");
tempCanvas.width = canvas.width;
tempCanvas.height = canvas.height;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.beginPath();
tempCtx.moveTo(path[0].x, path[0].y);
for (let i = 1; i < path.length; i++) {
tempCtx.lineTo(path[i].x, path[i].y);
}
tempCtx.closePath();
// قص فقط داخل المسار
tempCtx.clip();
tempCtx.drawImage(canvas, 0, 0);
// وضع الصورة الجديدة في الكانفس
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(tempCanvas, 0, 0);
}
📌 الوظيفة تأخذ المصفوفة path
المرسومة بالتحديد الحر أو النقاط.
🧩 2. تحويل الجزء المحدد إلى عنصر منفصل (layer-like)
function extractSelection(path) {
const bounds = getBounds(path); // احسب أبعاد التحديد
const imageData = ctx.getImageData(bounds.x, bounds.y, bounds.width, bounds.height);
// إنشاء عنصر جديد
const newCanvas = document.createElement("canvas");
newCanvas.width = bounds.width;
newCanvas.height = bounds.height;
const newCtx = newCanvas.getContext("2d");
newCtx.putImageData(imageData, 0, 0);
// يمكنك الآن تحريك newCanvas، أو حفظه، أو إضافته إلى الطبقات
document.body.appendChild(newCanvas); // كمثال
}
function getBounds(path) {
const xList = path.map(p => p.x);
const yList = path.map(p => p.y);
const x = Math.min(...xList);
const y = Math.min(...yList);
const width = Math.max(...xList) - x;
const height = Math.max(...yList) - y;
return {x, y, width, height};
}
📦 تحول التحديد إلى عنصر منفصل، يمكن استخدامه لاحقًا، تحريكه، أو معالجته.
هل تحب نضيف خيارات إضافية مثل:
تعبئة التحديد بلون؟
تطبيق فلاتر (Blur، Sharp، إلخ) داخل التحديد فقط؟
سحب التحديد كعنصر وتحريكه داخل الكانفس؟
الزر | الوظيفة | لماذا يُستخدم؟ | كيفية كتابته بشكل صحيح | اكتشاف الخطأ |
---|---|---|---|---|
📂 فتح | openImage() | تحميل صورة إلى الكانفس | تأكد من أن الدالة openImage() تُفتح نافذة تحديد ملفات أو تستخدم File API | هل الدالة معرفة؟ هل تتعامل مع أنواع الصور بشكل سليم؟ |
📋 نسخ | copyCanvas() | نسخ محتوى الكانفس | استخدم canvas.toDataURL() ثم انسخه إلى clipboard | هل أذونات clipboard مفعّلة؟ |
📥 لصق | pasteCanvas() | لصق صورة أو محتوى من الحافظة | استدعِ الحافظة باستخدام navigator.clipboard.read() | هل المتصفح يدعم clipboard API؟ |
🧹 حذف الكانفس | clearCanvas() | مسح كل شيء على الكانفس | استخدم clearRect لمسح الرسم | هل الكانفس مُعرف؟ هل السياق ctx موجود؟ |
✂️ قص | cropArea() | قص جزء محدد من الصورة | غالبًا يجب وجود آلية لتحديد المنطقة ثم اقتصاصها باستخدام canvas | هل منطق التحديد موجود؟ هل القص يحافظ على الجودة؟ |
📝 تعديل | editContent() | تعديل النصوص أو الرسومات | يمكن فتح محرر نصي أو أداة تعديل داخلية | هل نوع المحتوى قابل للتعديل؟ هل هناك modal جاهز؟ |
↩️ استرجاع | undo() | التراجع عن آخر خطوة | يمكن حفظ الحالة السابقة في stack واسترجاعها | هل سجل الخطوات موجود؟ هل الاسترجاع يعمل؟ |
↪️ التالي | redo() | إرجاع خطوة تم التراجع عنها | استدعِ الحالة من stack المرتبط بـ redo | هل يخزن الحالات بشكل صحيح؟ هل التكرار ممكن؟ |
💾 حفظ | saveImage() | حفظ الكانفس كصورة | استخدم toDataURL() أو toBlob() لحفظ الصورة | هل الامتداد صحيح؟ هل يمكن تنزيلها؟ |
❌ إغلاق | closeEditor() | غلق واجهة التحرير | إخفاء أو تدمير العنصر | هل توجد النافذة فعلاً؟ هل يغلق بطريقة سليمة؟ |
✏️ قلم رسم (حر) | activateFreeDraw() | تمكين المستخدم من الرسم الحر | الاستماع لأحداث الماوس وتحريك المؤشر على الكانفس | هل الأحداث تعمل؟ هل الرسم يُسجل؟ |
🖋️ بنطول (نقاط) | activatePenTool() | رسم نقاط أو خطوط مستقيمة | غالبًا بالنقر على نقاط وتوصيلها | هل يتم حساب المسارات بدقة؟ |
✨ تحديد ذكي | detectSmart() | استخدام خوارزميات لتحديد العناصر تلقائيًا | ربما تستخدم الذكاء الاصطناعي أو Edge Detection | هل توجد مكتبة ذكاء صناعي؟ هل الأداء جيد؟ |
🧠 إصلاح تلقائي | fixWithAI() | تصحيح تلقائي للصورة | يمكن استخدام خوارزمية لتحسين الجودة أو إزالة التشويش | هل الخوارزمية ناجحة؟ هل تظهر نتائج فورية؟ |
- الحصول على الرابط
- X
- بريد إلكتروني
- التطبيقات الأخرى
تعليقات
إرسال تعليق