أداة دمج الصور وتحويلها إلى فيديو MP4
بواسطة morbah |
|
محرر الفيديو الاحترافي لدمج الصور والموسيقى
اصنع فيديو عالي الجودة مع مؤثرات بصرية وصوتية مباشرة من متصفحك.
2. إعدادات المونتاج والتأثيرات
يرجى رفع الصورتين لتنشيط المحرر...
<div class="pro-video-editor">
<h2>محرر الفيديو الاحترافي لدمج الصور والموسيقى</h2>
<p class="subtitle">اصنع فيديو عالي الجودة مع مؤثرات بصرية وصوتية مباشرة من متصفحك.</p>
<!-- أقسام رفع الوسائط -->
<div class="editor-grid">
<div class="editor-section">
<h3>1. الصور والوسائط</h3>
<div class="file-row">
<div class="file-box">
<label>الصورة الأولى:</label>
<input type="file" id="img1" accept="image/*">
<div id="preview-1" class="img-preview"></div>
</div>
<div class="file-box">
<label>الصورة الثانية:</label>
<input type="file" id="img2" accept="image/*">
<div id="preview-2" class="img-preview"></div>
</div>
</div>
<div class="file-box audio-box" style="margin-top: 15px;">
<label>إضافة موسيقى خلفية (اختياري):</label>
<input type="file" id="audioInput" accept="audio/*">
<audio id="bgAudio" controls style="display:none; width:100%; margin-top:10px;"></audio>
</div>
</div>
<!-- إعدادات المونتاج والتأثيرات -->
<div class="editor-section">
<h3>2. إعدادات المونتاج والتأثيرات</h3>
<div class="control-row">
<label>جودة الفيديو:</label>
<select id="videoQuality">
<option value="720">HD (1280x720)</option>
<option value="1080" selected>Full HD (1920x1080)</option>
<option value="1440">2K (2560x1440)</option>
</select>
</div>
<div class="control-row">
<label>زمن عرض الصورة (ثانية):</label>
<input type="number" id="duration" value="3" min="1" max="15">
</div>
<div class="control-row">
<label>تأثير الانتقال:</label>
<select id="transitionType">
<option value="fade">تلاشي (Fade)</option>
<option value="slide">انزلاق لليسار (Slide Left)</option>
<option value="none">قطع مباشر (None)</option>
</select>
</div>
<div class="control-row">
<label>فلتر الألوان:</label>
<select id="colorFilter">
<option value="none">طبيعي</option>
<option value="grayscale(100%)">أبيض وأسود</option>
<option value="sepia(80%)">مظهر دافئ (Sepia)</option>
<option value="contrast(140%) brightness(110%)">تباين سينمائي</option>
<option value="hue-rotate(90deg)">ألوان سريالية</option>
</select>
</div>
</div>
</div>
<!-- إضافة نصوص على الفيديو -->
<div class="editor-section text-overlay-section" style="margin-top: 15px;">
<h3>3. الكتابة على الفيديو</h3>
<div class="text-controls">
<input type="text" id="overlayText" placeholder="اكتب نصاً يظهر على الفيديو...">
<div class="text-style-row">
<label>اللون: <input type="color" id="textColor" value="#ffffff"></label>
<label>الحجم: <input type="range" id="fontSize" min="20" max="120" value="50"></label>
<label>الموضع:
<select id="textPosition">
<option value="center">المنتصف</option>
<option value="top">الأعلى</option>
<option value="bottom">الأسفل</option>
</select>
</label>
</div>
</div>
</div>
<div class="action-zone">
<button id="generateBtn" disabled>ابدأ عملية الرندرة وتصدير MP4</button>
<div class="progress-container" id="progressContainer">
<div class="progress-bar" id="progressBar">0%</div>
</div>
<div id="status">يرجى رفع الصورتين لتنشيط المحرر...</div>
</div>
<!-- المعاينة والتحميل -->
<div class="result-section">
<video id="previewVideo" controls style="display:none;"></video>
<br>
<a class="download-btn" id="downloadLink" style="display:none;">تحميل الفيديو بجودتك المختارة MP4</a>
</div>
<canvas id="videoCanvas" style="display:none;"></canvas>
</div>
<style>
.pro-video-editor {
direction: rtl;
font-family: 'Segoe UI', Arial, sans-serif;
background: #1a1a1a;
color: #fff;
border-radius: 16px;
padding: 30px;
max-width: 900px;
margin: 30px auto;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.pro-video-editor h2 {
color: #00ffcc;
margin-top: 0;
font-size: 24px;
}
.pro-video-editor h3 {
color: #00ffcc;
border-bottom: 1px solid #333;
padding-bottom: 8px;
margin-top: 0;
font-size: 16px;
}
.subtitle {
color: #aaa;
font-size: 14px;
margin-bottom: 25px;
}
.editor-grid {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.editor-section {
flex: 1;
min-width: 300px;
background: #252525;
padding: 20px;
border-radius: 10px;
border: 1px solid #333;
}
.file-row {
display: flex;
gap: 10px;
}
.file-box {
flex: 1;
background: #1e1e1e;
padding: 10px;
border-radius: 8px;
border: 1px dashed #555;
font-size: 12px;
}
.file-box label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.file-box input[type="file"] {
width: 100%;
color: #ccc;
font-size: 11px;
}
.img-preview {
margin-top: 10px;
height: 80px;
background-size: contain;
background-repeat: no-repeat;
background-position: center;
background-color: #111;
border-radius: 4px;
}
.control-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.control-row select, .control-row input {
background: #333;
color: #fff;
border: 1px solid #555;
padding: 6px;
border-radius: 4px;
outline: none;
}
.text-controls input[type="text"] {
width: 100%;
padding: 10px;
background: #1e1e1e;
border: 1px solid #444;
color: white;
border-radius: 6px;
margin-bottom: 10px;
box-sizing: border-box;
}
.text-style-row {
display: flex;
gap: 15px;
font-size: 13px;
align-items: center;
flex-wrap: wrap;
}
.text-style-row select, .text-style-row input {
background: #333;
color: white;
border: 1px solid #555;
border-radius: 4px;
}
.action-zone {
margin-top: 25px;
text-align: center;
}
#generateBtn {
background: #00ffcc;
color: #111;
border: none;
padding: 14px 40px;
font-size: 16px;
font-weight: bold;
border-radius: 30px;
cursor: pointer;
transition: 0.3s;
}
#generateBtn:disabled {
background: #444;
color: #888;
cursor: not-allowed;
}
.progress-container {
width: 100%;
background-color: #333;
border-radius: 20px;
margin-top: 15px;
display: none;
overflow: hidden;
}
.progress-bar {
width: 0%;
height: 20px;
background-color: #00ffcc;
color: #111;
text-align: center;
line-height: 20px;
font-size: 12px;
font-weight: bold;
transition: width 0.1s linear;
}
#status {
margin-top: 10px;
font-size: 13px;
color: #aaa;
}
.result-section {
margin-top: 30px;
text-align: center;
}
#previewVideo {
max-width: 100%;
width: 640px;
border-radius: 12px;
border: 2px solid #333;
}
.download-btn {
display: inline-block;
margin-top: 15px;
background: #28a745;
color: white !important;
text-decoration: none;
padding: 12px 35px;
border-radius: 30px;
font-weight: bold;
box-shadow: 0 4px 15px rgba(40,167,69,0.4);
}
</style>
<script>
const img1Input = document.getElementById('img1');
const img2Input = document.getElementById('img2');
const audioInput = document.getElementById('audioInput');
const bgAudio = document.getElementById('bgAudio');
const generateBtn = document.getElementById('generateBtn');
const canvas = document.getElementById('videoCanvas');
const ctx = canvas.getContext('2d');
const status = document.getElementById('status');
const previewVideo = document.getElementById('previewVideo');
const downloadLink = document.getElementById('downloadLink');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
let img1 = new Image();
let img2 = new Image();
let img1Loaded = false;
let img2Loaded = false;
let audioFile = null;
function checkReady() {
if (img1Loaded && img2Loaded) {
generateBtn.disabled = false;
status.innerText = "الأدوات جاهزة للمونتاج! قم بضبط خياراتك واضغط رندرة.";
}
}
img1Input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (ev) => {
img1.onload = () => {
img1Loaded = true;
document.getElementById('preview-1').style.backgroundImage = `url('${ev.target.result}')`;
checkReady();
};
img1.src = ev.target.result;
};
reader.readAsDataURL(file);
}
});
img2Input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (ev) => {
img2.onload = () => {
img2Loaded = true;
document.getElementById('preview-2').style.backgroundImage = `url('${ev.target.result}')`;
checkReady();
};
img2.src = ev.target.result;
};
reader.readAsDataURL(file);
}
});
audioInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
audioFile = file;
bgAudio.src = URL.createObjectURL(file);
bgAudio.style.display = 'block';
}
});
generateBtn.addEventListener('click', async () => {
status.innerText = "جاري تحضير المحرك الرندرة المباشر...";
generateBtn.disabled = true;
previewVideo.style.display = 'none';
downloadLink.style.display = 'none';
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
progressBar.innerText = '0%';
// ضبط أبعاد الجودة المطلوبة
const quality = document.getElementById('videoQuality').value;
if (quality === '720') {
canvas.width = 1280; canvas.height = 720;
} else if (quality === '1080') {
canvas.width = 1920; canvas.height = 1080;
} else if (quality === '1440') {
canvas.width = 2560; canvas.height = 1440;
}
const durationSec = parseFloat(document.getElementById('duration').value) || 3;
const singleImgDurationMs = durationSec * 1000;
const totalDurationMs = singleImgDurationMs * 2; // الوقت الإجمالي الفعلي للفيديو بالملي ثانية
// التقاط بث الفيديو من الـ Canvas
const canvasStream = canvas.captureStream(30);
let outputStream = canvasStream;
let audioContext = null;
let audioSource = null;
let audioDestination = null;
// إذا تم رفع مقطع صوتي، نقوم بدمجه مع بث الفيديو
if (audioFile) {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
audioSource = audioContext.createMediaElementSource(bgAudio);
audioDestination = audioContext.createMediaStreamDestination();
audioSource.connect(audioDestination);
audioSource.connect(audioContext.destination); // للسماح بالاستماع أثناء المعالجة (يمكنك كتم صوت عنصر الصوت إذا رغبت)
const videoTrack = canvasStream.getVideoTracks()[0];
const audioTrack = audioDestination.stream.getAudioTracks()[0];
outputStream = new MediaStream([videoTrack, audioTrack]);
}
// دعم المتصفحات للتصدير
let options = { mimeType: 'video/mp4;codecs=avc1' };
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
options = { mimeType: 'video/webm;codecs=vp9' };
}
if (!MediaRecorder.isTypeSupported(options.mimeType)) {
options = { mimeType: 'video/webm' };
}
let mediaRecorder;
try {
mediaRecorder = new MediaRecorder(outputStream, options);
} catch (err) {
status.innerText = "المتصفح لا يدعم هذا الترميز: " + err.message;
generateBtn.disabled = false;
return;
}
const chunks = [];
mediaRecorder.ondataavailable = (e) => {
if (e.data.size > 0) chunks.push(e.data);
};
mediaRecorder.onstop = () => {
// إيقاف الصوت والمقاطع المفتوحة
if (audioFile) {
bgAudio.pause();
audioContext.close();
}
const blob = new Blob(chunks, { type: options.mimeType });
const videoURL = URL.createObjectURL(blob);
previewVideo.src = videoURL;
previewVideo.style.display = 'block';
downloadLink.href = videoURL;
downloadLink.download = `video_output_${quality}p.mp4`;
downloadLink.style.display = 'inline-block';
status.innerText = "اكتمل الرندرة بنجاح! جاهز للتحميل بجودة عالية.";
progressBar.innerText = "اكتمل 100%";
generateBtn.disabled = false;
};
// بدء تشغيل الصوت وتسجيل الفيديو تزامناً
if (audioFile) {
bgAudio.currentTime = 0;
bgAudio.play();
}
mediaRecorder.start();
const transitionType = document.getElementById('transitionType').value;
const filterVal = document.getElementById('colorFilter').value;
const overlayText = document.getElementById('overlayText').value.trim();
const textColor = document.getElementById('textColor').value;
const fontSizeFactor = parseInt(document.getElementById('fontSize').value);
const textPosition = document.getElementById('textPosition').value;
const startTime = performance.now();
function renderLoop(now) {
const elapsed = now - startTime;
const progressPercent = Math.min((elapsed / totalDurationMs) * 100, 100);
// تحديث شريط التقدم البرمجي للمستخدم
progressBar.style.width = `${progressPercent.toFixed(0)}%`;
progressBar.innerText = `${progressPercent.toFixed(0)}%`;
if (elapsed >= totalDurationMs) {
mediaRecorder.stop();
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
// تفعيل فلتر الألوان المختار
ctx.filter = filterVal;
// حساب أبعاد الصورتين للحفاظ على التناسب داخل الشاشة السوداء
function drawCenteredImage(img, opacity = 1, shiftX = 0) {
ctx.save();
ctx.globalAlpha = opacity;
const canvasRatio = canvas.width / canvas.height;
const imgRatio = img.naturalWidth / img.naturalHeight;
let drawWidth, drawHeight, offsetX, offsetY;
if (imgRatio > canvasRatio) {
drawWidth = canvas.width;
drawHeight = canvas.width / imgRatio;
offsetX = 0;
offsetY = (canvas.height - drawHeight) / 2;
} else {
drawHeight = canvas.height;
drawWidth = canvas.height * imgRatio;
offsetX = (canvas.width - drawWidth) / 2;
offsetY = 0;
}
ctx.drawImage(img, offsetX + shiftX, offsetY, drawWidth, drawHeight);
ctx.restore();
}
const transitionDuration = 1000; // مدة الانتقال بين الصورتين (1 ثانية)
const midPoint = singleImgDurationMs;
// رسم الخلفية السوداء الافتراضية للفيديو
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// معالجة الانتقالات (Transitions) بناءً على التوقيت الفعلي بالملي ثانية
if (elapsed < (midPoint - transitionDuration / 2)) {
// المرحلة الأولى: الصورة الأولى كاملة الوضوح
drawCenteredImage(img1);
} else if (elapsed > (midPoint + transitionDuration / 2)) {
// المرحلة الأخيرة: الصورة الثانية كاملة الوضوح
drawCenteredImage(img2);
} else {
// مرحلة الانتقال الحركي
const transElapsed = elapsed - (midPoint - transitionDuration / 2);
const transProgress = transElapsed / transitionDuration; // قيمة من 0 إلى 1
if (transitionType === 'fade') {
// انتقال تلاشي
drawCenteredImage(img1, 1 - transProgress);
drawCenteredImage(img2, transProgress);
} else if (transitionType === 'slide') {
// انتقال انزلاق لليسار
drawCenteredImage(img1, 1, -transProgress * canvas.width);
drawCenteredImage(img2, 1, (1 - transProgress) * canvas.width);
} else {
// قطع مباشر دون تأثير انتقال
if (transProgress < 0.5) {
drawCenteredImage(img1);
} else {
drawCenteredImage(img2);
}
}
}
// إلغاء الفلتر قبل كتابة النصوص لضمان بقائها بألوانها الأصلية والواضحة
ctx.filter = "none";
// رسم النص المدخل على الفيديو
if (overlayText !== "") {
ctx.save();
ctx.direction = 'rtl'; // دعم محاذاة اللغة العربية
ctx.fillStyle = textColor;
// قياس حجم الخط بناءً على أبعاد دقة الفيديو
const responsiveFontSize = (canvas.height / 1080) * fontSizeFactor;
ctx.font = `bold ${responsiveFontSize}px Arial, Tahoma`;
ctx.textAlign = 'center';
// إحداثيات موقع النص المختار
let textX = canvas.width / 2;
let textY = canvas.height / 2; // المنتصف افتراضياً
if (textPosition === 'top') {
textY = canvas.height * 0.15;
} else if (textPosition === 'bottom') {
textY = canvas.height * 0.85;
}
// إضافة ظل احترافي خلف النص لجعله مقروءاً على أي خلفية
ctx.shadowColor = "rgba(0, 0, 0, 0.8)";
ctx.shadowBlur = 12;
ctx.shadowOffsetX = 4;
ctx.shadowOffsetY = 4;
ctx.fillText(overlayText, textX, textY);
ctx.restore();
}
requestAnimationFrame(renderLoop);
}
// بدء حلقة الرندر الفعلية
requestAnimationFrame(renderLoop);
});
</script>
انا مهتم بمجال التقنية والربح من الانترنت واتطلع لنشر المزيد من المقالات التي تفيدكم
تعليقات
إرسال تعليق