MAX_ZOOM = 5; MIN_ZOOM = 0.1; SCROLL_SENSITIVITY = 0.0005; var PublishStories = { main: undefined, back: undefined, video: undefined, videoSelect: undefined, canvas: undefined, imageCanvas: undefined, canvasMinWidth: 1080, canvasMinHeight: 1920, cameraOffset: { x: 0, y: 0 }, cameraZoom: 1, isDragging: false, dragStart: { x: 0, y: 0 }, initialPinchDistance: null, lastZoom: 1, init: function () { PublishStories_ajaxGetContactsCount(); MovimTpl.loadingPage(); MovimTpl.pushAnchorState('story', function () { document.querySelector('#publishstories').classList = ''; PublishStories.close(); }); PublishStories.main = document.querySelector('#publishstories'); PublishStories.back = document.querySelector("#publishstories #publishstoriesback"); PublishStories.video = document.querySelector("#publishstories video"); PublishStories.videoSelect = document.querySelector("#publishstories select#publishstoriessource"); PublishStories.canvas = document.querySelector('#publishstories canvas'); PublishStories.main.addEventListener('mousemove', PublishStories.onMainPointerMove); PublishStories.canvas.addEventListener('mousedown', PublishStories.onPointerDown); PublishStories.canvas.addEventListener('touchstart', (e) => PublishStories.handleTouch(e, PublishStories.onPointerDown)); PublishStories.canvas.addEventListener('mouseup', PublishStories.onPointerUp); PublishStories.canvas.addEventListener('touchend', (e) => PublishStories.handleTouch(e, PublishStories.onPointerUp)); PublishStories.canvas.addEventListener('mousemove', PublishStories.onPointerMove); PublishStories.canvas.addEventListener('touchmove', (e) => PublishStories.handleTouch(e, PublishStories.onPointerMove)); PublishStories.canvas.addEventListener('wheel', (e) => PublishStories.adjustZoom(e.deltaY * SCROLL_SENSITIVITY)); PublishStories.main.querySelector('#publishstoriesswitch').onclick = () => { PublishStories.videoSelect.selectedIndex++; if (PublishStories.videoSelect.selectedIndex == -1) { PublishStories.videoSelect.selectedIndex = 0; } Toast.send(PublishStories.videoSelect.options[PublishStories.videoSelect.selectedIndex].label); PublishStories.getStream(); }; PublishStories.main.classList = 'show'; PublishStories.back.onclick = () => { history.back(); }; PublishStories.close(); PublishStories.getStream(true); }, draw: function () { if (PublishStories.imageCanvas == undefined) return; PublishStories.canvas.width = PublishStories.canvasMinWidth; PublishStories.canvas.height = PublishStories.canvasMinHeight; let context = PublishStories.canvas.getContext('2d'); ctx.clearRect(0, 0, PublishStories.canvas.width, PublishStories.canvas.height) context.fillStyle = MovimUtils.imageToHex(PublishStories.imageCanvas); context.fillRect(0, 0, PublishStories.canvas.width, PublishStories.canvas.height); // Let's move! context.translate(PublishStories.canvas.width / 2, PublishStories.canvas.height / 2); context.scale(PublishStories.cameraZoom, PublishStories.cameraZoom); context.translate( -PublishStories.canvas.width / 2 + PublishStories.cameraOffset.x, -PublishStories.canvas.height / 2 + PublishStories.cameraOffset.y ); let adjustedHeight = PublishStories.canvas.height > PublishStories.imageCanvas.height ? PublishStories.imageCanvas.height : PublishStories.canvas.height; let ratio = adjustedHeight / PublishStories.imageCanvas.height; context.drawImage( PublishStories.imageCanvas, PublishStories.canvas.width / 2 - PublishStories.imageCanvas.width * ratio / 2, PublishStories.canvas.height / 2 - adjustedHeight / 2, PublishStories.imageCanvas.width * ratio, adjustedHeight ); requestAnimationFrame(PublishStories.draw); }, goToPublish: function () { let title = PublishStories.main.querySelector('form textarea[name=title]'); if (title.value == '') { PublishStories_ajaxNoTitle(); return; } PublishStories.main.classList.add('publish'); }, goToEdit: function () { PublishStories.main.classList.remove('publish'); }, publish: function () { PublishStories.main.querySelector('#publishactions').classList.add('uploading'); PublishStories.canvas.toBlob( function (blob) { Upload.prepare(blob); Upload.name = 'story.jpg'; Upload.init(true); }, 'image/jpeg', 0.85 ); }, close: function () { if (!PublishStories.video) return; let stream = PublishStories.video.srcObject; if (stream) { stream.getTracks().forEach(track => track.stop()); } PublishStories.video.srcObject = null; PublishStories.imageCanvas = undefined; PublishStories.main.querySelector('form').reset(); PublishStories.main.querySelector('#publishactions').classList = ''; PublishStories.reset(); PublishStories.main.classList = ''; PublishStories.main == undefined; }, openImage: function () { PublishStories.main.querySelector('input[type=file]').click(); }, applyImage: function () { file = PublishStories.main.querySelector('input[type=file]').files[0]; if (!file.type.match(/image.*/)) { console.log("Not a picture !"); PublishStories.reset(); } else { var reader = new FileReader(); reader.readAsDataURL(file); reader.addEventListener('load', function (ev) { var image = new Image(); image.src = reader.result; image.onload = function () { PublishStories.drawMediaToImageCanvas(image, image.width, image.height, 1); PublishStories.draw(); PublishStories.main.classList = 'edit'; } }); }; }, shoot: function () { if (PublishStories.imageCapture) { PublishStories.imageCapture.grabFrame() .then(image => { PublishStories.drawMediaToImageCanvas( image, image.width, image.height, PublishStories.getRatioFromHeight(image.height) ); PublishStories.draw(); }) .catch(error => console.log(error)); } else { PublishStories.drawMediaToImageCanvas( PublishStories.video, PublishStories.video.videoWidth, PublishStories.video.videoHeight, PublishStories.getRatioFromHeight(PublishStories.video.videoHeight) ); PublishStories.video.pause(); PublishStories.draw(); } PublishStories.main.classList = 'edit'; }, getRatioFromHeight: function (height) { let ratio = PublishStories.canvasMinHeight / height; if (ratio < 1) ratio = 1; return ratio; }, drawMediaToImageCanvas: function (media, width, height, ratio) { PublishStories.imageCanvas = document.createElement('canvas'); PublishStories.imageCanvas.width = width * ratio; PublishStories.imageCanvas.height = height * ratio; var context = PublishStories.imageCanvas.getContext('2d'); context.drawImage(media, 0, 0, PublishStories.imageCanvas.width, PublishStories.imageCanvas.height); }, reset: function () { if (PublishStories.videoSelect.options.length > 0) { PublishStories.main.classList = 'shoot'; PublishStories.video.play(); } else { PublishStories.main.classList = 'shoot error'; } PublishStories.cameraOffset = { x: 0, y: 0 }; PublishStories.cameraZoom = PublishStories.lastZoom = 1; }, getStream: function (init) { PublishStories.main.classList = 'wait'; if (PublishStories.video.srcObject) { PublishStories.video.srcObject.getTracks().forEach(track => track.stop()); } const videoSource = PublishStories.videoSelect.value; const constraints = { video: { deviceId: videoSource ? { exact: videoSource } : undefined, width: { ideal: 1920 }, height: { ideal: 1920 } } }; navigator.mediaDevices.getUserMedia(constraints) .then(stream => { MovimTpl.finishedPage(); PublishStories.gotStream(stream); if (init) { navigator.mediaDevices.enumerateDevices().then(PublishStories.gotDevices); } }) .catch(e => { MovimTpl.finishedPage(); PublishStories.noStream() }); }, noStream: function (stream) { PublishStories.main.classList = 'shoot error'; }, gotStream: function (stream) { PublishStories.main.classList = 'shoot'; PublishStories.videoSelect.selectedIndex = [...PublishStories.videoSelect.options]. findIndex(option => option.text === stream.getVideoTracks()[0].label); PublishStories.video.srcObject = stream; PublishStories.video.play(); // We try to use ImageCapture if (typeof (ImageCapture) != 'undefined') { const track = stream.getVideoTracks()[0]; PublishStories.imageCapture = new ImageCapture(track); } // If we cancel after the authorization was given if (PublishStories.main.classList == '') { PublishStories.close(); }; }, gotDevices: function (devicesInfo) { PublishStories.videoSelect.innerHTML = ''; for (const deviceInfo of devicesInfo) { if (deviceInfo.kind === 'videoinput') { const option = document.createElement('option'); option.value = deviceInfo.deviceId; option.text = deviceInfo.label || `Camera ${PublishStories.videoSelect.length + 1}`; if (!PublishStories.videoSelect.querySelector('option[value="' + deviceInfo.deviceId + '"]')) { PublishStories.videoSelect.appendChild(option); } } } if (PublishStories.videoSelect.options.length >= 2) { document.querySelector("#publishstories #publishstoriesswitch").classList.add('enabled'); } }, onPointerDown: function (e) { PublishStories.isDragging = true PublishStories.dragStart.x = MovimUtils.getEventLocation(e).x / PublishStories.cameraZoom - PublishStories.cameraOffset.x PublishStories.dragStart.y = MovimUtils.getEventLocation(e).y / PublishStories.cameraZoom - PublishStories.cameraOffset.y }, onPointerUp: function (e) { PublishStories.isDragging = false PublishStories.initialPinchDistance = null PublishStories.lastZoom = PublishStories.cameraZoom }, onMainPointerMove: function (e) { if (!PublishStories.main.classList.contains('edit') || !MovimUtils.getEventLocation(e)) return; let borders = PublishStories.canvas.getBoundingClientRect(); if (MovimUtils.getEventLocation(e).x < borders.left || MovimUtils.getEventLocation(e).x > borders.right || MovimUtils.getEventLocation(e).y < borders.top || MovimUtils.getEventLocation(e).y > borders.bottom) { PublishStories.onPointerUp(e); } }, onPointerMove: function (e) { if (PublishStories.isDragging) { PublishStories.cameraOffset.x = MovimUtils.getEventLocation(e).x / PublishStories.cameraZoom - PublishStories.dragStart.x PublishStories.cameraOffset.y = MovimUtils.getEventLocation(e).y / PublishStories.cameraZoom - PublishStories.dragStart.y } }, handleTouch: function (e, singleTouchHandler) { if (e.touches.length == 1) { singleTouchHandler(e) } else if (e.type == "touchmove" && e.touches.length == 2) { PublishStories.isDragging = false PublishStories.handlePinch(e) } }, handlePinch: function (e) { e.preventDefault() let touch1 = { x: e.touches[0].clientX, y: e.touches[0].clientY } let touch2 = { x: e.touches[1].clientX, y: e.touches[1].clientY } // This is distance squared, but no need for an expensive sqrt as it's only used in ratio let currentDistance = (touch1.x - touch2.x) ** 2 + (touch1.y - touch2.y) ** 2 if (PublishStories.initialPinchDistance == null) { PublishStories.initialPinchDistance = currentDistance } else { PublishStories.adjustZoom(null, currentDistance / PublishStories.initialPinchDistance) } }, adjustZoom: function (zoomAmount, zoomFactor) { if (!PublishStories.isDragging) { if (zoomAmount) { PublishStories.cameraZoom += zoomAmount } else if (zoomFactor) { PublishStories.cameraZoom = zoomFactor * PublishStories.lastZoom } PublishStories.cameraZoom = Math.min(PublishStories.cameraZoom, MAX_ZOOM) PublishStories.cameraZoom = Math.max(PublishStories.cameraZoom, MIN_ZOOM) } } } MovimEvents.registerWindow('loaded', 'publishstories', () => { Upload.attach((file) => { if (PublishStories.main && PublishStories.main.classList.contains('publish')) { PublishStories.main.querySelector('#publishactions').classList.remove('uploading'); PublishStories.main.querySelector('#publishactions').classList.add('publishing'); PublishStories_ajaxPublish(MovimUtils.formToJson('metadata'), file.id); } }); Upload.fail(() => { PublishStories.main.querySelector('#publishactions').classList.remove('uploading'); }); Upload.progress((percent) => { if (PublishStories.main != undefined && PublishStories.main.classList.contains('publish')) { PublishStories.main.querySelector('#publishactionsprogress').innerHTML = percent + '%'; } }); });