From 1ba149b3e83c8d8f6b58526986eba7afab7457b3 Mon Sep 17 00:00:00 2001 From: JuliusHerrmann Date: Tue, 14 Dec 2021 02:26:20 +0100 Subject: [PATCH] finished gif creator --- package-lock.json | 19 +++++ package.json | 1 + .../visualizations/GIFGenerator.jsx | 63 +++++++++++----- src/Simulation/visualizations/Graph.jsx | 18 ++++- src/css/Dropdown.css | 1 + src/css/GIFGenerator.css | 73 +++++++++++++++++-- src/css/Graph.css | 21 ++++++ src/css/Model.css | 1 + src/css/Slider.css | 1 + yarn.lock | 9 ++- 10 files changed, 178 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index c34bc0a..95bdc66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "react-dom": "^17.0.2", "react-nvd3": "^0.5.7", "react-scripts": "4.0.3", + "reactjs-popup": "^2.0.5", "web-vitals": "^1.0.1" } }, @@ -15714,6 +15715,18 @@ "node": ">=0.10.0" } }, + "node_modules/reactjs-popup": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz", + "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -33056,6 +33069,12 @@ } } }, + "reactjs-popup": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz", + "integrity": "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==", + "requires": {} + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", diff --git a/package.json b/package.json index 2336b35..ee76de9 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "react-dom": "^17.0.2", "react-nvd3": "^0.5.7", "react-scripts": "4.0.3", + "reactjs-popup": "^2.0.5", "web-vitals": "^1.0.1" }, "scripts": { diff --git a/src/Simulation/visualizations/GIFGenerator.jsx b/src/Simulation/visualizations/GIFGenerator.jsx index 4b2c4f0..7bf8dc9 100644 --- a/src/Simulation/visualizations/GIFGenerator.jsx +++ b/src/Simulation/visualizations/GIFGenerator.jsx @@ -11,8 +11,13 @@ class GIFGenerator extends React.Component { this.animationId = 0; } + generateGIF = () => { //clean input + if(!Math.abs(Number(this.state.duration)) > 0) { + console.log("Only numbers are allowed in duration!"); + return; + } this.setState({duration: Math.abs(this.state.duration)}); var gif = new GIF( { workers: 2, @@ -20,26 +25,38 @@ class GIFGenerator extends React.Component { repeat: this.state.loop? 0 : -1, background: this.state.background }); + + //set download trigger gif.on('finished', function(blob) { - window.open(URL.createObjectURL(blob)); + const link = document.createElement('a'); + // create a blobURI pointing to our Blob + link.href = URL.createObjectURL(blob); + link.download = "simulationGIF"; + // some browser needs the anchor to be in the doc + document.body.append(link); + link.click(); + link.remove(); + // in case the Blob uses a lot of memory + setTimeout(() => URL.revokeObjectURL(link.href), 7000); }); - let stepBehtmlFore = this.state.step; - + + let stepBefore = this.state.step; + //clear playing animation clearInterval(this.animationId); this.props.setState({step: 0}, () => { this.stepTime = 1; //this.animationId = setInterval(this.visualizeOneStep, this.stepTime); - this.animationId = setInterval(this.grabImageAndNextStep.bind(null, gif, stepBehtmlFore), this.stepTime); + this.animationId = setInterval(this.grabImageAndNextStep.bind(null, gif, stepBefore), this.stepTime); this.props.setState({playing: true}); }); } - grabImageAndNextStep = (gif, stepBehtmlFore) => { + grabImageAndNextStep = (gif, stepBefore) => { //check if we are at the end if (this.props.state.step > this.props.animationLength) { - this.props.setState({step: stepBehtmlFore}); + this.props.setState({step: stepBefore}); this.props.visualizeOneStep(false); clearInterval(this.animationId); gif.render(); @@ -57,18 +74,28 @@ class GIFGenerator extends React.Component { render() { return (
- - this.setState({duration: e.target.value})}/> - - this.setState({background: e.target.value})}/> - - - this.setState({loop: e.target.checked})}/> - +
+ GIF Generator +
+
+ + this.setState({duration: e.target.value})}/> +
+ +
+ + this.setState({background: e.target.value})}/> +
+
+ + + this.setState({loop: e.target.checked})}/> +
+
); } diff --git a/src/Simulation/visualizations/Graph.jsx b/src/Simulation/visualizations/Graph.jsx index 58470a2..195dfe2 100644 --- a/src/Simulation/visualizations/Graph.jsx +++ b/src/Simulation/visualizations/Graph.jsx @@ -1,5 +1,6 @@ import React from 'react'; import CytoscapeComponent from 'react-cytoscapejs'; +import Popup from 'reactjs-popup'; import '../../css/Graph.css'; import Slider from '../Slider'; import GIFGenerator from './GIFGenerator'; @@ -201,14 +202,23 @@ class Graph extends React.Component { //we want this to be a "tabbed" approach return (
-
-

Duration (seconds):

- + Generate GIF 📸} modal> + {close => ( +
+ + +
+ )} +
+
+

Duration (seconds):

+
{ this.cy = cy }} elements={this.props.graphData}/> - + +
); } } diff --git a/src/css/Dropdown.css b/src/css/Dropdown.css index 7efa745..08338e6 100644 --- a/src/css/Dropdown.css +++ b/src/css/Dropdown.css @@ -5,6 +5,7 @@ } .Dropdown { + cursor: pointer; color: #eeeeee; font-size: 15pt; display: block; diff --git a/src/css/GIFGenerator.css b/src/css/GIFGenerator.css index 72a2616..04e0c91 100644 --- a/src/css/GIFGenerator.css +++ b/src/css/GIFGenerator.css @@ -1,6 +1,69 @@ -#generateGIFPopup { - position: absolute; - top: 0; - left: 0; - border: solid black; +.popup-overlay { + background: rgba(0, 0, 0, 0.5); +} +.popup-content { + background-color: #2d4059; + /*background-color: #222831;*/ + padding: 1em; + border-radius: 30px; + filter: opacity(1); + max-width: 85%; +} +.modal { + font-size: 13pt; + font-family: 'Roboto', sans-serif; + color: #eeeeee; text-align: left; +} +.modal > .close { + color: white; + cursor: pointer; + position: absolute; + display: block; + padding: 2px 5px; + line-height: 20px; + right: -5px; + top: -5px; + font-size: 24px; + background: #ff5722; + border-radius: 18px; + border: 1px solid #ff5722; +} +.popupHeader { + font-size: 20pt; +} +.popupSection { + display: block; + margin-top: 10px; +} +.popupButton { + cursor: pointer; + margin: auto; + font-size: 16pt; + display: block; + margin-top: 10px; + border: none; + outline: none; + color: #eeeeee; + background-color: #0a8e4e; + border-radius: 10px; + padding: 5px 10px; + margin-top: 10px; +} +#gifLengthInput { + display: inline-block; + width: 1.55em; + /*transform: translateY(-3px);*/ + margin-left: 2%; + background-color: #222831; + color: #ff5722; + border-color: #ff5722; + border-style: none; + border-radius: 3px; + font-size: 12pt; + outline: solid; +} + +.gifLengthInput:focus { + outline-width: 0; + outline: solid; } diff --git a/src/css/Graph.css b/src/css/Graph.css index ffd275f..a8926d1 100644 --- a/src/css/Graph.css +++ b/src/css/Graph.css @@ -1,4 +1,5 @@ #recalculate { + cursor: pointer; margin-right: 20px; margin-bottom: 20px; border: none; @@ -11,6 +12,7 @@ } #switchView { + cursor: pointer; margin-right: 20px; margin-bottom: 20px; border: none; @@ -25,7 +27,9 @@ } #runSimulationButton { + cursor: pointer; margin-right: 20px; + margin-bottom: 20px; border: none; outline: none; font-size: 25pt; @@ -37,6 +41,23 @@ min-width: 295px; } +#gifGeneratorButton { + cursor: pointer; + border: none; + outline: none; + font-size: 25pt; + color: #eeeeee; + background-color: #0A8E4E; + border-radius: 10px; + padding: 5px 10px; + text-align: left; + min-width: 260px; +} + +#durationWrapper { + margin-top: -10px; +} + #durationDescription { display: inline-block; margin-right: 20px; diff --git a/src/css/Model.css b/src/css/Model.css index fd4feaa..11f4f3f 100644 --- a/src/css/Model.css +++ b/src/css/Model.css @@ -3,6 +3,7 @@ } .ColorPicker { + cursor: pointer; display: inline-block; margin-left: 150px; margin-top: 5px; diff --git a/src/css/Slider.css b/src/css/Slider.css index 6811c03..d47364a 100644 --- a/src/css/Slider.css +++ b/src/css/Slider.css @@ -41,6 +41,7 @@ input[type="range"]::-webkit-slider-thumb { } input[type="range"]::-moz-range-thumb { + cursor: pointer; height: 14px; width: 14px; background: #ff5722; diff --git a/yarn.lock b/yarn.lock index 16ab365..0f89a51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9345,7 +9345,7 @@ "strip-ansi" "6.0.0" "text-table" "0.2.0" -"react-dom@*", "react-dom@^17.0.2", "react-dom@>=0.14.0", "react-dom@>=15.0.0": +"react-dom@*", "react-dom@^17.0.2", "react-dom@>=0.14.0", "react-dom@>=15.0.0", "react-dom@>=16": "integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==" "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" "version" "17.0.2" @@ -9448,7 +9448,7 @@ optionalDependencies: "fsevents" "^2.1.3" -"react@*", "react@^17.0.2", "react@>= 16", "react@>=0.14.0", "react@>=15.0.0", "react@17.0.2": +"react@*", "react@^17.0.2", "react@>= 16", "react@>=0.14.0", "react@>=15.0.0", "react@>=16", "react@17.0.2": "integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==" "resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz" "version" "17.0.2" @@ -9456,6 +9456,11 @@ "loose-envify" "^1.1.0" "object-assign" "^4.1.1" +"reactjs-popup@^2.0.5": + "integrity" "sha512-b5hv9a6aGsHEHXFAgPO5s1Jw1eSkopueyUVxQewGdLgqk2eW0IVXZrPRpHR629YcgIpC2oxtX8OOZ8a7bQJbxA==" + "resolved" "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.5.tgz" + "version" "2.0.5" + "read-pkg-up@^7.0.1": "integrity" "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==" "resolved" "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz"