Author: | yyosifov |
---|---|
Official Page: | Go to website |
Publish Date: | November 18, 2017 |
License: | MIT |
Description:
A Cross-Platform, blazing fast, extendable image Viewer based on Electron, Angular 4, and TypeScript.
More Features:
- Loads a directory of images in a second
- Let’s you manipulate image with the keyboard – next,previous,rotate,copy,delete
- Easily extendable – new functionality can be added at any time
- Supports all image files a browser does
- Runs cross-platform. Mainly useful for OS X though.
Installation:
# NPM $ npm install image-viewer --save
Usage:
The markup.
<div class="div-image"> <div id='div-center-container' class='div-center-container'> <button id="open-file" type="button" class="btn btn-secondary-outline btn-lg">Open</button> <img id="currentImage" /> </div> <div id="control-panel"> <div class="control-buttons text-center"> <div> <button type="button" class="btn btn-primary" id="rotate-left"><i class="fa fa-undo fa-1x"></i></button> <button type="button" class="btn btn-success" id="previous"><i class="fa fa-arrow-circle-left fa-1x"></i></button> <label style="margin-left: 5px; margin-right: 5px; color: white;" id="directoryStats"></label> <button type="button" class="btn btn-success" id="next"><i class="fa fa-arrow-circle-right fa-1x"></i></button> <button type="button" class="btn btn-primary" id="rotate-right"><i class="fa fa-repeat fa-1x"></i></button> </div> </div> </div> </div>
The example app.js.
'use strict'; var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var remote = require('electron').remote; const { dialog } = require('electron').remote; var ipcRenderer = require('electron').ipcRenderer; var fileSystem = require('./js/file-system'); var constants = require('./js/constants'); // jquery selectors var $currentImage = $('#currentImage'), $previous = $('#previous'), $next = $('#next'), $directoryStats = $('#directoryStats'), $openFile = $('#open-file'), $controlPanel = $('#control-panel'), $rotateLeft = $('#rotate-left'), $rotateRight = $('#rotate-right'), $divCenterImage = $('#div-center-container'), containerDimensions = {}; // the list of all retrieved files var imageFiles = [], currentImageFile = '', currentDir = ''; // migrated var toggleButtons = function(hasSelectedImage) { // disable buttons? if(hasSelectedImage) { $openFile.hide(); $currentImage.show(); $controlPanel.show(); } else { $openFile.show(); $currentImage.hide(); $controlPanel.hide(); } }; // Shows an image on the page. var showImage = function(index) { toggleButtons(true); setRotateDegrees(0); // clear the CSS $currentImage.css({ height: '100%', width: '100%' }); // clean up containerDimensions = {}; $currentImage.data('currentIndex', index); $currentImage.attr('src', imageFiles[index]); currentImageFile = imageFiles[index]; // Hide show previous/next if there are no more/less files. // $next.toggle(!(index + 1 === imageFiles.length)); // $previous.toggle(!(index === 0)); // set the stats text var statsText = (index + 1) + ' / ' + imageFiles.length; $directoryStats.text(statsText); ipcRenderer.send('image-changed', currentImageFile); }; var hasImages = function() { return imageFiles && imageFiles.length > 0; } var onPreviousClick = function() { if(!hasImages()) { return; } var currentImageId = $currentImage.data('currentIndex'); if(currentImageId > 0) { showImage(--currentImageId); } else { // we're at 0 -> move to the end. showImage(imageFiles.length - 1); } }; $previous.click(onPreviousClick); var onNextClick = function() { if(!hasImages()) { return; } var currentImageId = $currentImage.data('currentIndex'); if(currentImageId + 1 < imageFiles.length) { showImage(++currentImageId); } else { // we're at the end - next is the beginning showImage(0); } }; $next.click(onNextClick); // Show image in Full screen on double click var fullscreenButton = document.getElementById("currentImage"); fullscreenButton.addEventListener("dblclick", toggleFullScreen, false); function toggleFullScreen() { //console.log('double click...'); ipcRenderer.send('toggle-full-screen'); } var _loadDir = function(dir, fileName) { currentDir = dir; imageFiles = fileSystem.getDirectoryImageFiles(dir); var selectedImageIndex = imageFiles.indexOf(fileName); if(selectedImageIndex === -1) { selectedImageIndex = 0; } if(selectedImageIndex < imageFiles.length) { showImage(selectedImageIndex); } else { alert('No image files found in this directory.'); } }; var onOpen = function(filePath) { filePath = filePath + ''; // convert to string var stat = fs.lstatSync(filePath); if(stat.isDirectory()) { onDirOpen(filePath); } else { onFileOpen(filePath); } }; var onFileOpen = function(fileName) { fileName = fileName + ''; // convert to string. var dirName = path.dirname(fileName); _loadDir(dirName, fileName); }; var onDirOpen = function(dir) { _loadDir(dir + ''); // convert to string }; var onFileDelete = function() { // file has been deleted, show previous or next... var index = imageFiles.indexOf(currentImageFile); if(index > -1) { imageFiles.splice(index, 1); } if(index === imageFiles.length) index--; if(index < 0) { // no more images in this directory - it's empty... toggleButtons(false); } else { showImage(index); } }; var getCurrentFile = function() { return currentImageFile; }; var getPreviousDegrees = function() { var deg = $currentImage.data('rotateDegree') || 0; return deg; }; var getDimensionIndex = function(deg) { //console.log(`get ${deg}`); return (deg == 0 || Math.abs(deg) == 180) ? 0 : 1; } var setRotateDegrees = function(deg) { if (!currentImage.src) { // nothing to rotate return; } const imgHeight = $currentImage.height(), imgWidth = $currentImage.width(); const containerHeight = $divCenterImage.height(), containerWidth = $divCenterImage.width(); //console.log(`imgHeight = ${imgHeight}, imgWidth = ${imgWidth}`); const prevDegrees = getPreviousDegrees(); const dimensionsIndex = getDimensionIndex(prevDegrees); if(!containerDimensions[dimensionsIndex]) { // persist them containerDimensions[dimensionsIndex] = { height: containerHeight, width: containerWidth }; //console.log(`set ${dimensionsIndex} in container dimensions: h:` + containerDimensions[dimensionsIndex].height + ' and w:' + containerDimensions[dimensionsIndex].width); } const otherIndex = (dimensionsIndex + 1) % 2; if(containerDimensions[otherIndex]) { //console.log('in container dimensions: h:' + containerDimensions[otherIndex].height + ' and w:' + containerDimensions[otherIndex].width); $currentImage.css({ height: containerDimensions[otherIndex].height, width: containerDimensions[otherIndex].width }); } else { containerDimensions[otherIndex] = { height: containerWidth, width: containerHeight } //console.log(`set ${otherIndex} in container dimensions: h:` + containerDimensions[otherIndex].height + ' and w:' + containerDimensions[otherIndex].width); $currentImage.css({ height: containerWidth, width: containerHeight }); } $currentImage.css({ '-webkit-transform' : 'rotate('+deg+'deg)', '-moz-transform' : 'rotate('+deg+'deg)', '-ms-transform' : 'rotate('+deg+'deg)', '-o-transform' : 'rotate('+deg+'deg)', 'transform' : 'rotate('+deg+'deg)', 'zoom' : 1 }); $currentImage.data('rotateDegree', deg); }; var onRotate = function(rotationDegrees) { // get current degree and rotationDegrees var deg = $currentImage.data('rotateDegree') || 0; deg -= rotationDegrees; deg = deg % 360; setRotateDegrees(deg); }; $rotateLeft.click(function() { onRotate(-90); }); $rotateRight.click(function() { onRotate(90); }); // Initialize the app var initialize = function() { var appMenu = require('./js/app-menu'); appMenu.initialize({ onOpen: onOpen, onFileDelete: onFileDelete, getCurrentFile: getCurrentFile }); // no files selected toggleButtons(false); //ipcRenderer.send('log', '$openFile = ' + $openFile); $openFile.click(function() { //ipcRenderer.send('log', 'dialog = ' + dialog); // TODO: Refactor this... code duplication dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory' ], filters: [ { name: 'Images', extensions: constants.SupportedImageExtensions } ] }, function(fileName) { if(fileName) { onOpen(fileName); } }); }); // handle navigation from left/right clicks $(window).keydown(function(ev) { switch(ev.keyCode) { case constants.LeftKey: onPreviousClick(); break; case constants.RightKey: onNextClick(); break; case constants.UpKey: onRotate(-90); break; case constants.DownKey: onRotate(90); break; case constants.EscapeKey: ipcRenderer.send('exit-full-screen', currentImageFile); break; } }); }; initialize();