update camera dan otorisator
This commit is contained in:
585
resources/views/surveyor/js/camera-editor.blade.php
Normal file
585
resources/views/surveyor/js/camera-editor.blade.php
Normal file
@@ -0,0 +1,585 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
class UniversalCameraEditor {
|
||||
constructor() {
|
||||
// Initialize elements
|
||||
this.video = document.getElementById('video');
|
||||
this.canvas = document.getElementById('canvas');
|
||||
this.drawingCanvas = document.getElementById('drawingCanvas');
|
||||
this.ctx = this.drawingCanvas.getContext('2d');
|
||||
|
||||
// Buttons
|
||||
this.startButton = document.getElementById('startButton');
|
||||
this.takePhotoButton = document.getElementById('takePhotoButton');
|
||||
this.switchButton = document.getElementById('switchButton');
|
||||
this.backButton = document.getElementById('backButton');
|
||||
this.saveButton = document.getElementById('saveButton');
|
||||
this.undoButton = document.getElementById('undoButton');
|
||||
this.resetButton = document.getElementById('resetButton');
|
||||
|
||||
// Drawing controls
|
||||
this.colorPicker = document.getElementById('colorPicker');
|
||||
this.brushSize = document.getElementById('brushSize');
|
||||
this.brushSizeValue = document.getElementById('brushSizeValue');
|
||||
|
||||
// Text modal elements
|
||||
this.textModal = document.getElementById('textModal');
|
||||
this.textInput = document.getElementById('textInput');
|
||||
this.confirmTextBtn = document.getElementById('confirmTextBtn');
|
||||
this.cancelTextBtn = document.getElementById('cancelTextBtn');
|
||||
|
||||
// Initialize state
|
||||
this.stream = null;
|
||||
this.currentFacingMode = 'environment';
|
||||
this.isDrawing = false;
|
||||
this.currentTool = 'brush';
|
||||
this.drawingHistory = [];
|
||||
this.historyIndex = -1;
|
||||
|
||||
// Drawing coordinates
|
||||
this.startX = 0;
|
||||
this.startY = 0;
|
||||
this.lastX = 0;
|
||||
this.lastY = 0;
|
||||
|
||||
// Setup
|
||||
this.setupCanvas();
|
||||
this.setupEventListeners();
|
||||
this.setupDrawingContext();
|
||||
|
||||
|
||||
// Add text overlay container
|
||||
this.textOverlay = document.getElementById('textOverlay');
|
||||
this.activeTextElement = null;
|
||||
this.isDraggingText = false;
|
||||
this.textOffset = {
|
||||
x: 0,
|
||||
y: 0
|
||||
};
|
||||
|
||||
|
||||
// Current input field reference
|
||||
this.currentInputField = null;
|
||||
|
||||
}
|
||||
|
||||
setupCanvas() {
|
||||
// Set initial canvas size
|
||||
this.canvas.width = 1280;
|
||||
this.canvas.height = 960;
|
||||
this.drawingCanvas.width = 1280;
|
||||
this.drawingCanvas.height = 960;
|
||||
}
|
||||
|
||||
setupDrawingContext() {
|
||||
this.ctx.strokeStyle = this.colorPicker.value;
|
||||
this.ctx.lineWidth = this.brushSize.value;
|
||||
this.ctx.lineCap = 'round';
|
||||
this.ctx.lineJoin = 'round';
|
||||
}
|
||||
|
||||
async startCamera() {
|
||||
try {
|
||||
if (this.stream) {
|
||||
this.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
const constraints = {
|
||||
video: {
|
||||
facingMode: this.currentFacingMode,
|
||||
width: {
|
||||
ideal: 1280
|
||||
},
|
||||
height: {
|
||||
ideal: 960
|
||||
}
|
||||
},
|
||||
audio: false
|
||||
};
|
||||
|
||||
this.stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
this.video.srcObject = this.stream;
|
||||
|
||||
// Enable buttons after camera starts
|
||||
this.takePhotoButton.disabled = false;
|
||||
this.switchButton.disabled = false;
|
||||
|
||||
// Show video, hide canvas
|
||||
this.video.style.display = 'block';
|
||||
this.canvas.style.display = 'none';
|
||||
this.drawingCanvas.style.display = 'none';
|
||||
|
||||
// Hide editor controls
|
||||
document.getElementById('editorControls').classList.add('hidden');
|
||||
document.getElementById('cameraControls').classList.remove('hidden');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error accessing camera:', error);
|
||||
alert('Error accessing camera. Please make sure you have granted camera permissions.');
|
||||
}
|
||||
}
|
||||
|
||||
takePhoto() {
|
||||
// Draw video frame to canvas
|
||||
const context = this.canvas.getContext('2d');
|
||||
context.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Hide video, show canvases
|
||||
this.video.style.display = 'none';
|
||||
this.canvas.style.display = 'block';
|
||||
this.drawingCanvas.style.display = 'block';
|
||||
|
||||
// Show editor controls, hide camera controls
|
||||
document.getElementById('editorControls').classList.remove('hidden');
|
||||
document.getElementById('cameraControls').classList.add('hidden');
|
||||
|
||||
// Clear drawing history
|
||||
this.drawingHistory = [];
|
||||
this.historyIndex = -1;
|
||||
this.saveDrawingState();
|
||||
}
|
||||
|
||||
switchCamera() {
|
||||
this.currentFacingMode = this.currentFacingMode === 'environment' ? 'user' : 'environment';
|
||||
this.startCamera();
|
||||
}
|
||||
|
||||
resetToCamera() {
|
||||
// Clear canvases
|
||||
const context = this.canvas.getContext('2d');
|
||||
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
|
||||
// Reset history
|
||||
this.drawingHistory = [];
|
||||
this.historyIndex = -1;
|
||||
|
||||
// Restart camera
|
||||
this.startCamera();
|
||||
}
|
||||
|
||||
saveDrawingState() {
|
||||
// Trim history if we're not at the end
|
||||
if (this.historyIndex < this.drawingHistory.length - 1) {
|
||||
this.drawingHistory = this.drawingHistory.slice(0, this.historyIndex + 1);
|
||||
}
|
||||
|
||||
// Save current state
|
||||
this.drawingHistory.push(this.drawingCanvas.toDataURL());
|
||||
this.historyIndex++;
|
||||
}
|
||||
|
||||
undo() {
|
||||
if (this.historyIndex > 0) {
|
||||
this.historyIndex--;
|
||||
this.redrawHistory();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
resetDrawing() {
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
this.saveDrawingState();
|
||||
}
|
||||
|
||||
// Implement all event listeners from previous code here
|
||||
setupEventListeners() {
|
||||
// Camera controls
|
||||
this.startButton.onclick = () => this.startCamera();
|
||||
this.takePhotoButton.onclick = () => this.takePhoto();
|
||||
this.switchButton.onclick = () => this.switchCamera();
|
||||
this.backButton.onclick = () => this.resetToCamera();
|
||||
|
||||
// Drawing controls
|
||||
this.setupDrawingEvents();
|
||||
this.setupToolButtons();
|
||||
|
||||
// Settings changes
|
||||
this.colorPicker.oninput = (e) => this.ctx.strokeStyle = e.target.value;
|
||||
this.brushSize.oninput = (e) => {
|
||||
this.ctx.lineWidth = e.target.value;
|
||||
this.brushSizeValue.textContent = `${e.target.value}px`;
|
||||
};
|
||||
|
||||
// History controls
|
||||
this.undoButton.onclick = () => this.undo();
|
||||
this.resetButton.onclick = () => this.resetDrawing();
|
||||
|
||||
// Save functionality
|
||||
this.saveButton.onclick = () => this.saveAndUpload();
|
||||
|
||||
|
||||
// Text tool modal controls
|
||||
this.confirmTextBtn.onclick = () => {
|
||||
const text = this.textInput.value.trim();
|
||||
if (text) {
|
||||
this.addDraggableText(text);
|
||||
this.textInput.value = '';
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
setupToolButtons() {
|
||||
const toolButtons = document.querySelectorAll('.tool-btn');
|
||||
toolButtons.forEach(button => {
|
||||
button.onclick = () => {
|
||||
toolButtons.forEach(btn => btn.classList.remove('active'));
|
||||
button.classList.add('active');
|
||||
this.currentTool = button.dataset.tool;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Implement drawing events from previous code here
|
||||
setupDrawingEvents() {
|
||||
// Mouse events
|
||||
this.drawingCanvas.addEventListener('mousedown', (e) => this.startDrawing(e));
|
||||
this.drawingCanvas.addEventListener('mousemove', (e) => this.draw(e));
|
||||
this.drawingCanvas.addEventListener('mouseup', () => this.stopDrawing());
|
||||
this.drawingCanvas.addEventListener('mouseout', () => this.stopDrawing());
|
||||
|
||||
// Touch events
|
||||
this.drawingCanvas.addEventListener('touchstart', (e) => {
|
||||
if (this.shouldPreventDefault(e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const touch = e.touches[0];
|
||||
const mouseEvent = new MouseEvent('mousedown', {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
this.startDrawing(mouseEvent);
|
||||
}, {
|
||||
passive: false
|
||||
});
|
||||
|
||||
this.drawingCanvas.addEventListener('touchmove', (e) => {
|
||||
if (this.shouldPreventDefault(e)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const touch = e.touches[0];
|
||||
const mouseEvent = new MouseEvent('mousemove', {
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY
|
||||
});
|
||||
this.draw(mouseEvent);
|
||||
}, {
|
||||
passive: false
|
||||
});
|
||||
|
||||
this.drawingCanvas.addEventListener('touchend', () => {
|
||||
this.stopDrawing();
|
||||
}, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
// Implement drawing methods from previous code here
|
||||
startDrawing(e) {
|
||||
this.isDrawing = true;
|
||||
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||
const scaleX = this.drawingCanvas.width / rect.width;
|
||||
const scaleY = this.drawingCanvas.height / rect.height;
|
||||
|
||||
this.startX = (e.clientX - rect.left) * scaleX;
|
||||
this.startY = (e.clientY - rect.top) * scaleY;
|
||||
this.lastX = this.startX;
|
||||
this.lastY = this.startY;
|
||||
|
||||
if (this.currentTool === 'brush') {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.startX, this.startY);
|
||||
}
|
||||
}
|
||||
|
||||
addDraggableText(text) {
|
||||
const textElement = document.createElement('div');
|
||||
textElement.className = 'draggable-text';
|
||||
textElement.style.cssText = `
|
||||
position: absolute;
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
color: ${this.colorPicker.value};
|
||||
font-size: ${this.brushSize.value * 2}px;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`;
|
||||
|
||||
// Create text span
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.textContent = text;
|
||||
textElement.appendChild(textSpan);
|
||||
|
||||
// Create delete button
|
||||
const deleteBtn = document.createElement('button');
|
||||
deleteBtn.innerHTML = '×';
|
||||
deleteBtn.style.cssText = `
|
||||
border: none;
|
||||
background: none;
|
||||
color: #ff4444;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
`;
|
||||
textElement.appendChild(deleteBtn);
|
||||
|
||||
// Show/hide delete button on hover
|
||||
textElement.addEventListener('mouseenter', () => {
|
||||
deleteBtn.style.opacity = '1';
|
||||
});
|
||||
textElement.addEventListener('mouseleave', () => {
|
||||
deleteBtn.style.opacity = '0';
|
||||
});
|
||||
|
||||
// Delete functionality
|
||||
deleteBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
textElement.remove();
|
||||
this.saveDrawingState();
|
||||
});
|
||||
|
||||
// Center the text initially
|
||||
textElement.style.left = '50%';
|
||||
textElement.style.top = '50%';
|
||||
textElement.style.transform = 'translate(-50%, -50%)';
|
||||
|
||||
// Add drag functionality
|
||||
this.setupDraggableText(textElement);
|
||||
|
||||
this.textOverlay.appendChild(textElement);
|
||||
this.saveDrawingState();
|
||||
}
|
||||
|
||||
setupDraggableText(element) {
|
||||
let isDragging = false;
|
||||
let currentX;
|
||||
let currentY;
|
||||
let initialX;
|
||||
let initialY;
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
|
||||
const dragStart = (e) => {
|
||||
if (this.currentTool === 'text') {
|
||||
const event = e.type === 'mousedown' ? e : e.touches[0];
|
||||
initialX = event.clientX - xOffset;
|
||||
initialY = event.clientY - yOffset;
|
||||
|
||||
if (e.target === element || e.target.parentNode === element) {
|
||||
isDragging = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const drag = (e) => {
|
||||
if (isDragging) {
|
||||
e.preventDefault();
|
||||
const event = e.type === 'mousemove' ? e : e.touches[0];
|
||||
|
||||
currentX = event.clientX - initialX;
|
||||
currentY = event.clientY - initialY;
|
||||
|
||||
xOffset = currentX;
|
||||
yOffset = currentY;
|
||||
|
||||
setTranslate(currentX, currentY, element);
|
||||
}
|
||||
};
|
||||
|
||||
const dragEnd = () => {
|
||||
if (isDragging) {
|
||||
isDragging = false;
|
||||
this.saveDrawingState();
|
||||
}
|
||||
};
|
||||
|
||||
const setTranslate = (xPos, yPos, el) => {
|
||||
el.style.transform = `translate(${xPos}px, ${yPos}px)`;
|
||||
};
|
||||
|
||||
// Mouse events
|
||||
element.addEventListener('mousedown', dragStart);
|
||||
document.addEventListener('mousemove', drag);
|
||||
document.addEventListener('mouseup', dragEnd);
|
||||
|
||||
// Touch events
|
||||
element.addEventListener('touchstart', dragStart);
|
||||
document.addEventListener('touchmove', drag);
|
||||
document.addEventListener('touchend', dragEnd);
|
||||
}
|
||||
|
||||
// Add this CSS to your stylesheet
|
||||
|
||||
|
||||
draw(e) {
|
||||
if (!this.isDrawing) return;
|
||||
|
||||
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||
const scaleX = this.drawingCanvas.width / rect.width;
|
||||
const scaleY = this.drawingCanvas.height / rect.height;
|
||||
const x = (e.clientX - rect.left) * scaleX;
|
||||
const y = (e.clientY - rect.top) * scaleY;
|
||||
|
||||
switch (this.currentTool) {
|
||||
case 'arrow':
|
||||
// Restore the previous state before drawing new arrow
|
||||
if (this.historyIndex >= 0) {
|
||||
this.redrawHistory();
|
||||
} else {
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
}
|
||||
this.drawArrow(this.startX, this.startY, x, y);
|
||||
break;
|
||||
|
||||
case 'brush':
|
||||
this.ctx.lineTo(x, y);
|
||||
this.ctx.stroke();
|
||||
break;
|
||||
|
||||
case 'rectangle':
|
||||
// Restore the previous state before drawing new rectangle
|
||||
if (this.historyIndex >= 0) {
|
||||
this.redrawHistory();
|
||||
} else {
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
}
|
||||
this.ctx.beginPath();
|
||||
this.ctx.strokeRect(this.startX, this.startY, x - this.startX, y - this.startY);
|
||||
break;
|
||||
|
||||
case 'circle':
|
||||
// Restore the previous state before drawing new circle
|
||||
if (this.historyIndex >= 0) {
|
||||
this.redrawHistory();
|
||||
} else {
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
}
|
||||
const radius = Math.sqrt(Math.pow(x - this.startX, 2) + Math.pow(y - this.startY, 2));
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(this.startX, this.startY, radius, 0, Math.PI * 2);
|
||||
this.ctx.stroke();
|
||||
break;
|
||||
}
|
||||
|
||||
this.lastX = x;
|
||||
this.lastY = y;
|
||||
}
|
||||
|
||||
drawArrow(fromX, fromY, toX, toY) {
|
||||
const headLength = 20;
|
||||
const headAngle = Math.PI / 6;
|
||||
|
||||
// Calculate angle
|
||||
const angle = Math.atan2(toY - fromY, toX - fromX);
|
||||
|
||||
// Draw main line
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(fromX, fromY);
|
||||
this.ctx.lineTo(toX, toY);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw arrowhead
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(toX, toY);
|
||||
this.ctx.lineTo(
|
||||
toX - headLength * Math.cos(angle - headAngle),
|
||||
toY - headLength * Math.sin(angle - headAngle)
|
||||
);
|
||||
this.ctx.moveTo(toX, toY);
|
||||
this.ctx.lineTo(
|
||||
toX - headLength * Math.cos(angle + headAngle),
|
||||
toY - headLength * Math.sin(angle + headAngle)
|
||||
);
|
||||
this.ctx.stroke();
|
||||
}
|
||||
|
||||
startDrawing(e) {
|
||||
this.isDrawing = true;
|
||||
const rect = this.drawingCanvas.getBoundingClientRect();
|
||||
const scaleX = this.drawingCanvas.width / rect.width;
|
||||
const scaleY = this.drawingCanvas.height / rect.height;
|
||||
|
||||
this.startX = (e.clientX - rect.left) * scaleX;
|
||||
this.startY = (e.clientY - rect.top) * scaleY;
|
||||
this.lastX = this.startX;
|
||||
this.lastY = this.startY;
|
||||
|
||||
if (this.currentTool === 'brush') {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.startX, this.startY);
|
||||
}
|
||||
}
|
||||
|
||||
stopDrawing() {
|
||||
if (this.isDrawing) {
|
||||
this.isDrawing = false;
|
||||
if (this.currentTool === 'brush') {
|
||||
this.ctx.closePath();
|
||||
}
|
||||
this.saveDrawingState();
|
||||
}
|
||||
}
|
||||
|
||||
redrawHistory() {
|
||||
if (this.historyIndex >= 0 && this.drawingHistory[this.historyIndex]) {
|
||||
const img = new Image();
|
||||
img.src = this.drawingHistory[this.historyIndex];
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
this.ctx.drawImage(img, 0, 0);
|
||||
} else {
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
}
|
||||
}
|
||||
closeEditor() {
|
||||
if (this.stream) {
|
||||
this.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
// Clear canvases and text overlay
|
||||
this.ctx.clearRect(0, 0, this.drawingCanvas.width, this.drawingCanvas.height);
|
||||
const mainCtx = this.canvas.getContext('2d');
|
||||
mainCtx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.textOverlay.innerHTML = '';
|
||||
|
||||
// Reset state
|
||||
this.drawingHistory = [];
|
||||
this.historyIndex = -1;
|
||||
this.currentInputField = null;
|
||||
|
||||
// Close modals
|
||||
this.cameraModal.hide();
|
||||
this.textInputModal.hide();
|
||||
}
|
||||
|
||||
// Override this method in your implementation
|
||||
saveAndUpload() {
|
||||
|
||||
}
|
||||
|
||||
closeCamera() {
|
||||
// Stop the video stream
|
||||
if (this.stream) {
|
||||
this.stream.getTracks().forEach(track => track.stop());
|
||||
}
|
||||
|
||||
// Hide video and canvas elements
|
||||
this.video.style.display = 'none';
|
||||
this.canvas.style.display = 'none';
|
||||
this.drawingCanvas.style.display = 'none';
|
||||
|
||||
|
||||
// Reset any additional state if needed
|
||||
this.stream = null;
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
Reference in New Issue
Block a user