A GUI to help you to visualize trigonometric values. Built using ReactJS, SVG and Fraction.js (https://github.com/infusion/Fraction.js).
A Pen by Anthony Dugois on CodePen.
| <div | |
| class="ad-App" | |
| id="app"> | |
| </div> |
| const Component = React.Component | |
| class Angle extends Component { | |
| propTypes = { | |
| index: React.PropTypes.number.isRequired, | |
| isDragging: React.PropTypes.number.isRequired, | |
| angle: React.PropTypes.shape({ | |
| numerator: React.PropTypes.number.isRequired, | |
| denominator: React.PropTypes.number.isRequired, | |
| }).isRequired, | |
| dragStart: React.PropTypes.func.isRequired, | |
| circleRadius: React.PropTypes.number.isRequired, | |
| sketchSize: React.PropTypes.number.isRequired, | |
| } | |
| handleMouseDown = (e) => { | |
| this.props.dragStart(e, this.props.index) | |
| } | |
| render() { | |
| const { | |
| angle, | |
| index, | |
| isDragging, | |
| ...props, | |
| } = this.props | |
| const sketchHalfSize = props.sketchSize / 2 | |
| const numerator = angle.numerator !== "" ? angle.numerator : 1 | |
| const denominator = angle.denominator !== "" ? angle.denominator : 1 | |
| const radians = (numerator / denominator) * Math.PI | |
| const cosRadius = sketchHalfSize + Math.cos(radians) * props.circleRadius | |
| const sinRadius = sketchHalfSize - Math.sin(radians) * props.circleRadius | |
| const angleRadius = 40 | |
| const cosAngleRadius = sketchHalfSize + Math.cos(radians) * angleRadius | |
| const sinAngleRadius = sketchHalfSize - Math.sin(radians) * angleRadius | |
| return ( | |
| <g className="ad-SketchAngle"> | |
| <path | |
| className="ad-SketchAngle-angle" | |
| d={ | |
| "M " + (sketchHalfSize + angleRadius) + " " + sketchHalfSize + | |
| " A " + angleRadius + " " + angleRadius + ", " + | |
| (Math.sin(radians) < 0 ? "0, 1, 0" : "0, 0, 0") + ", " + | |
| cosAngleRadius + " " + sinAngleRadius + | |
| " L " + sketchHalfSize + " " + sketchHalfSize + | |
| " Z" | |
| } /> | |
| <g className="ad-SketchAngle-trigo"> | |
| <line | |
| className="ad-SketchAngle-cos" | |
| x1={ sketchHalfSize } | |
| y1={ sinRadius } | |
| x2={ cosRadius } | |
| y2={ sinRadius } /> | |
| <line | |
| className="ad-SketchAngle-sin" | |
| x1={ cosRadius } | |
| y1={ sketchHalfSize } | |
| x2={ cosRadius } | |
| y2={ sinRadius } /> | |
| </g> | |
| <line | |
| className="ad-SketchAngle-line" | |
| x1={ sketchHalfSize } | |
| y1={ sketchHalfSize } | |
| x2={ sketchHalfSize + props.circleRadius } | |
| y2={ sketchHalfSize } /> | |
| <line | |
| className="ad-SketchAngle-line" | |
| x1={ sketchHalfSize } | |
| y1={ sketchHalfSize } | |
| x2={ cosRadius } | |
| y2={ sinRadius } /> | |
| <circle | |
| className={ | |
| "ad-SketchAngle-dot" + | |
| (isDragging === index ? " is-dragging" : "") | |
| } | |
| onMouseDown={ this.handleMouseDown } | |
| cx={ cosRadius } | |
| cy={ sinRadius } | |
| r={ 6 } /> | |
| </g> | |
| ) | |
| } | |
| } | |
| class Sketch extends Component { | |
| propTypes = { | |
| angles: React.PropTypes.array.isRequired, | |
| circleRadius: React.PropTypes.number.isRequired, | |
| sketchSize: React.PropTypes.number.isRequired, | |
| } | |
| render() { | |
| const { | |
| angles, | |
| ...props, | |
| } = this.props | |
| const sketchHalfSize = props.sketchSize / 2 | |
| const svgAngles = angles.map((angle, index) => { | |
| return (<Angle | |
| angle={ angle } | |
| index={ index } | |
| { ...props } />) | |
| }) | |
| return ( | |
| <svg | |
| className="ad-Sketch" | |
| viewBox={ "0 0 " + props.sketchSize + " " + props.sketchSize }> | |
| <g className="ad-Sketch-base"> | |
| <line | |
| className="ad-Sketch-ortho" | |
| x1={ sketchHalfSize } | |
| y1={ 0 } | |
| x2={ sketchHalfSize } | |
| y2={ props.sketchSize } /> | |
| <line | |
| className="ad-Sketch-ortho" | |
| x1={ 0 } | |
| y1={ sketchHalfSize } | |
| x2={ props.sketchSize } | |
| y2={ sketchHalfSize } /> | |
| <text | |
| className="ad-Sketch-hint" | |
| x={ sketchHalfSize + 10 } | |
| y={ 10 }> | |
| sin | |
| </text> | |
| <text | |
| className="ad-Sketch-hint ad-Sketch-hint--r" | |
| x={ props.sketchSize - 5 } | |
| y={ sketchHalfSize - 10 }> | |
| cos | |
| </text> | |
| <text | |
| className="ad-Sketch-value" | |
| x={ sketchHalfSize + props.circleRadius + 15 } | |
| y={ sketchHalfSize - 10 }> | |
| 0 | |
| </text> | |
| <text | |
| className="ad-Sketch-value ad-Sketch-value--r" | |
| x={ sketchHalfSize - (props.circleRadius + 15) } | |
| y={ sketchHalfSize - 10 }> | |
| π | |
| </text> | |
| <text | |
| className="ad-Sketch-value ad-Sketch-value--c ad-Sketch-value--t" | |
| x={ sketchHalfSize } | |
| y={ sketchHalfSize - (props.circleRadius + 10) }> | |
| π / 2 | |
| </text> | |
| <text | |
| className="ad-Sketch-value ad-Sketch-value--c ad-Sketch-value--b" | |
| x={ sketchHalfSize } | |
| y={ sketchHalfSize + props.circleRadius + 10 }> | |
| 3π / 2 | |
| </text> | |
| <circle | |
| className="ad-Sketch-circle" | |
| cx={ sketchHalfSize } | |
| cy={ sketchHalfSize } | |
| r={ props.circleRadius } /> | |
| </g> | |
| <g className="ad-Sketch-angles"> | |
| { svgAngles } | |
| </g> | |
| </svg> | |
| ) | |
| } | |
| } | |
| class Icon extends Component { | |
| propTypes = { | |
| name: React.PropTypes.string.isRequired, | |
| } | |
| render() { | |
| let path | |
| switch (this.props.name) { | |
| case "clear": | |
| path = "M810 274l-238 238 238 238-60 60-238-238-238 238-60-60 238-238-238-238 60-60 238 238 238-238z" | |
| break; | |
| case "add": | |
| path = "M810 554h-256v256h-84v-256h-256v-84h256v-256h84v256h256v84z" | |
| break; | |
| } | |
| return ( | |
| <svg | |
| className="ad-Icon" | |
| viewBox="0 0 1024 1024"> | |
| <path d={ path } /> | |
| </svg> | |
| ) | |
| } | |
| } | |
| class Button extends Component { | |
| propTypes = { | |
| type: React.PropTypes.string, | |
| size: React.PropTypes.string, | |
| icon: React.PropTypes.string, | |
| } | |
| render() { | |
| const { | |
| type, | |
| size, | |
| icon, | |
| children, | |
| ...props, | |
| } = this.props | |
| return ( | |
| <button | |
| className={ | |
| "ad-Button" + | |
| (type ? " ad-Button--" + type : "") + | |
| (size ? " ad-Button--" + size : "") | |
| } | |
| { ...props } | |
| type="button"> | |
| { icon && (<Icon name={ icon } />) } | |
| { | |
| children && ( | |
| <span className="ad-Button-text"> | |
| { children } | |
| </span> | |
| ) | |
| } | |
| </button> | |
| ) | |
| } | |
| } | |
| class FormGroup extends Component { | |
| propTypes = { | |
| index: React.PropTypes.number.isRequired, | |
| angle: React.PropTypes.shape({ | |
| numerator: React.PropTypes.number.isRequired, | |
| denominator: React.PropTypes.number.isRequired, | |
| }).isRequired, | |
| updateNumerator: React.PropTypes.func.isRequired, | |
| updateDenominator: React.PropTypes.func.isRequired, | |
| deleteFormGroup: React.PropTypes.func.isRequired, | |
| } | |
| handleNumerator = (e) => { | |
| this.props.updateNumerator(this.props.index, e.target.value) | |
| } | |
| handleDenominator = (e) => { | |
| this.props.updateDenominator(this.props.index, e.target.value) | |
| } | |
| handleClick = (e) => { | |
| e.preventDefault() | |
| this.props.deleteFormGroup(this.props.index) | |
| } | |
| render() { | |
| return ( | |
| <div className="ad-FormGroup"> | |
| <div className="ad-FormGroup-color"></div> | |
| <div className="ad-FormMath"> | |
| <div className="ad-FormMath-frac"> | |
| <div className="ad-FormMath-n"> | |
| <input | |
| className="ad-FormInput" | |
| ref="numerator" | |
| value={ this.props.angle.numerator } | |
| onChange={ this.handleNumerator } | |
| type="text" /> | |
| </div> | |
| <div className="ad-FormMath-n"> | |
| <input | |
| className="ad-FormInput" | |
| ref="denominator" | |
| value={ this.props.angle.denominator } | |
| onChange={ this.handleDenominator } | |
| type="text" /> | |
| </div> | |
| </div> | |
| <div className="ad-FormMath-formula"> | |
| π | |
| </div> | |
| </div> | |
| <div className="ad-FormGroup-action"> | |
| <Button | |
| onClick={ this.handleClick } | |
| type="cancel" | |
| size="mini" | |
| icon="clear" /> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| } | |
| class Form extends Component { | |
| propTypes = { | |
| angles: React.PropTypes.array.isRequired, | |
| shouldScroll: React.PropTypes.bool.isRequired, | |
| addFormGroup: React.PropTypes.func.isRequired, | |
| blurAddButton: React.PropTypes.func.isRequired, | |
| } | |
| componentDidUpdate() { | |
| const n = React.findDOMNode(this.refs.groups) | |
| if (this.props.shouldScroll) { | |
| n.scrollTop = n.scrollHeight | |
| } | |
| } | |
| handleClick = (e) => { | |
| e.preventDefault() | |
| this.props.addFormGroup() | |
| } | |
| handleBlur = (e) => { | |
| this.props.blurAddButton() | |
| } | |
| render() { | |
| const { | |
| angles, | |
| addFormGroup, | |
| ...props, | |
| } = this.props | |
| let groups = angles.map((angle, index) => { | |
| return ( | |
| <FormGroup | |
| index={ index } | |
| angle={ angle } | |
| { ...props } /> | |
| ) | |
| }) | |
| return ( | |
| <form className="ad-Form"> | |
| <div | |
| className="ad-Form-groups" | |
| ref="groups"> | |
| { groups } | |
| </div> | |
| <div className="ad-Form-actions"> | |
| <Button | |
| onClick={ this.handleClick } | |
| onBlur={ this.handleBlur } | |
| type="primary" | |
| size="full" | |
| icon="add"> | |
| Add angle | |
| </Button> | |
| </div> | |
| </form> | |
| ) | |
| } | |
| } | |
| class Trigonometry extends Component { | |
| state = { | |
| isDragging: false, | |
| shouldScroll: false, | |
| angles: [ | |
| { | |
| numerator: 7, | |
| denominator: 10, | |
| }, | |
| { | |
| numerator: 3, | |
| denominator: 2, | |
| }, | |
| { | |
| numerator: 1, | |
| denominator: 5, | |
| }, | |
| ], | |
| } | |
| updateNumerator = (index, numerator) => { | |
| if (numerator !== "") { | |
| numerator = parseFloat(numerator) | |
| } | |
| const angles = this.state.angles.map((angle, angleIndex) => { | |
| if (angleIndex === index) { | |
| numerator = (numerator !== "" && isNaN(numerator)) ? angle.numerator : numerator | |
| return { | |
| numerator: numerator, | |
| denominator: angle.denominator, | |
| } | |
| } | |
| return angle | |
| }) | |
| this.setState({ angles }) | |
| } | |
| updateDenominator = (index, denominator) => { | |
| if (denominator !== "") { | |
| denominator = parseFloat(denominator) | |
| if (denominator === 0) { | |
| denominator = 1 | |
| } | |
| } | |
| const angles = this.state.angles.map((angle, angleIndex) => { | |
| if (angleIndex === index) { | |
| denominator = (denominator !== "" && isNaN(denominator)) ? angle.denominator : denominator | |
| return { | |
| numerator: angle.numerator, | |
| denominator: denominator, | |
| } | |
| } | |
| return angle | |
| }) | |
| this.setState({ angles }) | |
| } | |
| blurAddButton = () => { | |
| this.setState({ | |
| shouldScroll: false, | |
| }) | |
| } | |
| addFormGroup = () => { | |
| const numerator = 0, | |
| denominator = 1, | |
| angles = this.state.angles | |
| angles.push({ numerator, denominator }) | |
| this.setState({ | |
| angles, | |
| shouldScroll: true, | |
| }) | |
| } | |
| deleteFormGroup = (index) => { | |
| let angles = this.state.angles | |
| delete angles[index] | |
| this.setState({ angles }) | |
| } | |
| drag = (e) => { | |
| let i = this.state.isDragging | |
| let sketch = React.findDOMNode(this.refs.sketch).getBoundingClientRect() | |
| if (i !== false) { | |
| const sketchHalfSize = this.props.sketchSize / 2 | |
| let angles = this.state.angles, | |
| x = (e.pageX - sketch.left) - sketchHalfSize, | |
| y = sketchHalfSize - (e.pageY - sketch.top), | |
| rad = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)), | |
| sine = y / rad, | |
| cosine = x / rad, | |
| theta | |
| theta = Math.acos(cosine) | |
| if (sine < 0) { | |
| theta = 2 * Math.PI - theta | |
| } | |
| const f = new Fraction((theta / Math.PI).toFixed(1)) | |
| angles[i] = { | |
| numerator: f.n, | |
| denominator: f.d, | |
| } | |
| this.setState({ angles }) | |
| } | |
| } | |
| dragStart = (e, index) => { | |
| e.preventDefault() | |
| this.setState({ | |
| isDragging: index, | |
| }) | |
| } | |
| dragEnd = (e) => { | |
| this.setState({ | |
| isDragging: false, | |
| }) | |
| } | |
| render() { | |
| return ( | |
| <div> | |
| <div className="ad-App-head"> | |
| <h1 className="ad-App-title"> | |
| Trigonometry Helper | |
| </h1> | |
| <div className="ad-App-hint"> | |
| Type values to move an angle or drag it directly on the scheme. | |
| </div> | |
| </div> | |
| <div | |
| className="ad-Trigonometry" | |
| onMouseUp={ this.dragEnd } | |
| onMouseMove={ this.drag }> | |
| <div className="ad-Trigonometry-svg"> | |
| <Sketch | |
| ref="sketch" | |
| angles={ this.state.angles } | |
| drag={ this.drag } | |
| dragStart={ this.dragStart } | |
| dragEnd={ this.dragEnd } | |
| isDragging={ this.state.isDragging } | |
| { ...this.props } /> | |
| </div> | |
| <div className="ad-Trigonometry-form"> | |
| <Form | |
| angles={ this.state.angles } | |
| shouldScroll={ this.state.shouldScroll } | |
| updateNumerator={ this.updateNumerator } | |
| updateDenominator={ this.updateDenominator } | |
| blurAddButton={ this.blurAddButton } | |
| addFormGroup={ this.addFormGroup } | |
| deleteFormGroup={ this.deleteFormGroup } /> | |
| </div> | |
| </div> | |
| <div className="ad-App-foot"> | |
| <a href="https://twitter.com/a_dugois"> | |
| Follow me on Twitter | |
| </a> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| } | |
| React.render( | |
| <Trigonometry | |
| circleRadius={ 130 } | |
| sketchSize={ 26 * 16 } />, | |
| document.querySelector("#app") | |
| ) |
| <script src="//cdnjs.cloudflare.com/ajax/libs/react/0.13.0/react.min.js"></script> | |
| <script src="//s3-us-west-2.amazonaws.com/s.cdpn.io/80862/fraction.js"></script> |
| @use cssnext; | |
| @import url(https://fonts.googleapis.com/css?family=Open+Sans:400,400italic,600,600italic); | |
| :root { | |
| --colorPalette-1: #37474F; | |
| --colorPalette-2: #263238; | |
| --colorPalette-3: #00BCD4; | |
| } | |
| ::-webkit-scrollbar { | |
| width: .5rem; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--colorPalette-1); | |
| border-radius: 10px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: color(var(--colorPalette-1) l(+5%)); | |
| } | |
| html { | |
| font-size: 16px; | |
| } | |
| html, body { | |
| height: 100%; | |
| } | |
| *, | |
| *::before, | |
| *::after { | |
| box-sizing: border-box; | |
| } | |
| .ad-Icon { | |
| width: 1.5em; | |
| height: 1.5em; | |
| color: currentColor; | |
| } | |
| .ad-Icon path { | |
| fill: currentColor; | |
| } | |
| .ad-Button { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding: .7rem 1rem; | |
| border: 2px solid var(--colorPalette-1); | |
| border-radius: 4px; | |
| background: none; | |
| cursor: pointer; | |
| transition: border .1s, | |
| background .1s, | |
| color .1s; | |
| font-size: .7rem; | |
| color: var(--colorPalette-1); | |
| } | |
| .ad-Button:focus { | |
| outline: 0; | |
| } | |
| .ad-Button--full { | |
| width: 100%; | |
| } | |
| .ad-Button--mini { | |
| padding: .25rem; | |
| font-size: .5rem; | |
| } | |
| .ad-Button--primary { | |
| border-color: var(--colorPalette-3); | |
| color: var(--colorPalette-3); | |
| } | |
| .ad-Button--primary:focus, | |
| .ad-Button--primary:hover { | |
| background: var(--colorPalette-3); | |
| color: #fff; | |
| } | |
| .ad-Button--cancel { | |
| border-color: #fff; | |
| color: #fff; | |
| } | |
| .ad-Button--cancel:focus, | |
| .ad-Button--cancel:hover { | |
| background: #fff; | |
| color: var(--colorPalette-2); | |
| } | |
| .ad-Button-text { | |
| text-transform: uppercase; | |
| font-family: "Open Sans", sans-serif; | |
| font-weight: bold; | |
| color: currentColor; | |
| } | |
| .ad-Icon + .ad-Button-text { | |
| margin-left: .25rem; | |
| } | |
| .ad-App { | |
| height: 100%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: var(--colorPalette-3); | |
| } | |
| .ad-App-head { | |
| margin-bottom: 2rem; | |
| } | |
| .ad-App-title { | |
| font-family: "Open Sans", sans-serif; | |
| font-weight: bold; | |
| font-size: 1.05rem; | |
| color: #fff; | |
| } | |
| .ad-App-hint { | |
| margin-top: .4rem; | |
| font-family: "Open Sans", sans-serif; | |
| font-size: .9rem; | |
| color: #fff; | |
| } | |
| .ad-App-foot { | |
| margin-top: 1rem; | |
| text-transform: uppercase; | |
| text-align: right; | |
| font-family: "Open Sans", sans-serif; | |
| font-weight: bold; | |
| font-size: .65rem; | |
| } | |
| .ad-App-foot a { | |
| color: #fff; | |
| text-decoration: underline; | |
| } | |
| .ad-Trigonometry { | |
| overflow: hidden; | |
| display: flex; | |
| height: 30rem; | |
| background: var(--colorPalette-1); | |
| border-radius: 4px; | |
| box-shadow: 0 2px 6px rgba(0, 0, 0, .4); | |
| } | |
| .ad-Trigonometry-svg { | |
| width: 30rem; | |
| height: 100%; | |
| padding: 2rem; | |
| } | |
| .ad-Trigonometry-form { | |
| width: 14rem; | |
| height: 100%; | |
| background: var(--colorPalette-2); | |
| } | |
| .ad-Sketch { | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .ad-Sketch-circle { | |
| stroke: #fff; | |
| stroke-width: 2px; | |
| fill: none; | |
| } | |
| .ad-Sketch-ortho { | |
| stroke: color(var(--colorPalette-1) l(+5%)); | |
| stroke-width: 2px; | |
| } | |
| .ad-Sketch-hint { | |
| fill: #fff; | |
| font-family: "Open Sans", sans-serif; | |
| font-style: italic; | |
| font-size: .75rem; | |
| } | |
| .ad-Sketch-value { | |
| fill: #fff; | |
| font-family: "Open Sans", sans-serif; | |
| font-weight: bold; | |
| font-size: .8rem; | |
| } | |
| .ad-Sketch-value--c { | |
| text-anchor: middle; | |
| } | |
| .ad-Sketch-value--t { | |
| alignment-baseline: text-after-edge; | |
| } | |
| .ad-Sketch-value--b { | |
| alignment-baseline: text-before-edge; | |
| } | |
| .ad-Sketch-hint--r, | |
| .ad-Sketch-value--r { | |
| text-anchor: end; | |
| } | |
| .ad-SketchAngle:nth-child(5n+1), | |
| .ad-FormGroup:nth-child(5n+1) { | |
| color: #2196F3; | |
| } | |
| .ad-SketchAngle:nth-child(5n+2), | |
| .ad-FormGroup:nth-child(5n+2) { | |
| color: #66BB6A; | |
| } | |
| .ad-SketchAngle:nth-child(5n+3), | |
| .ad-FormGroup:nth-child(5n+3) { | |
| color: #F44336; | |
| } | |
| .ad-SketchAngle:nth-child(5n+4), | |
| .ad-FormGroup:nth-child(5n+4) { | |
| color: #EC407A; | |
| } | |
| .ad-SketchAngle:nth-child(5n+5), | |
| .ad-FormGroup:nth-child(5n+5) { | |
| color: #FFEB3B; | |
| } | |
| .ad-SketchAngle-line { | |
| stroke: currentColor; | |
| stroke-width: 2px; | |
| stroke-linecap: round; | |
| } | |
| .ad-SketchAngle-angle { | |
| opacity: .2; | |
| fill: currentColor; | |
| } | |
| .ad-SketchAngle-trigo { | |
| stroke: color(var(--colorPalette-1) l(+5%)); | |
| stroke-width: 2px; | |
| stroke-dasharray: 6, 8; | |
| } | |
| .ad-SketchAngle-dot { | |
| fill: currentColor; | |
| stroke: #fff; | |
| stroke-width: 2px; | |
| transition: stroke .2s, | |
| stroke-width .2s; | |
| } | |
| .ad-SketchAngle-dot.is-dragging { | |
| stroke: #fff; | |
| stroke-width: 4px; | |
| } | |
| .ad-Form { | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .ad-Form-groups { | |
| flex: 1; | |
| overflow: auto; | |
| padding: 1rem 2rem 0; | |
| } | |
| .ad-Form-actions { | |
| padding: 2rem; | |
| } | |
| .ad-FormGroup { | |
| width: 100%; | |
| padding: 1rem 0; | |
| display: flex; | |
| align-items: center; | |
| } | |
| .ad-FormGroup + .ad-FormGroup { | |
| border-top: 1px solid var(--colorPalette-1); | |
| } | |
| .ad-FormGroup-color { | |
| width: 12px; | |
| height: 12px; | |
| border: 2px solid #fff; | |
| border-radius: 50%; | |
| background: currentColor; | |
| } | |
| .ad-FormMath { | |
| margin-left: .8rem; | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| } | |
| .ad-FormMath-frac { | |
| width: 2.5rem; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .ad-FormMath-n + .ad-FormMath-n { | |
| margin-top: .25rem; | |
| padding-top: .25rem; | |
| border-top: 2px solid #fff; | |
| } | |
| .ad-FormMath-formula { | |
| flex: 1; | |
| margin-left: .4rem; | |
| cursor: default; | |
| font-family: "Open Sans", sans-serif; | |
| font-size: 1.2rem; | |
| color: #fff; | |
| } | |
| .ad-FormInput { | |
| width: 100%; | |
| padding: .25rem; | |
| border: none; | |
| border-radius: 4px; | |
| background: var(--colorPalette-1); | |
| transition: background .1s; | |
| text-align: center; | |
| font-family: "Open Sans", sans-serif; | |
| font-size: .85rem; | |
| color: #fff; | |
| } | |
| .ad-FormInput:focus { | |
| outline: 0; | |
| background: color(var(--colorPalette-1) l(+10%)); | |
| } |
A GUI to help you to visualize trigonometric values. Built using ReactJS, SVG and Fraction.js (https://github.com/infusion/Fraction.js).
A Pen by Anthony Dugois on CodePen.