├── .editorconfig ├── .gitignore ├── .rollup.js ├── .tape.js ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── INSTALL.md ├── LICENSE.md ├── README.md ├── package.json ├── src ├── index.js └── lib │ ├── is.js │ ├── new.js │ └── transforms.js └── test ├── basic.css ├── basic.expect.css └── basic.precision.expect.css /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,md,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.editorconfig 3 | !.gitignore 4 | !.rollup.js 5 | !.tape.js 6 | !.travis.yml 7 | *.log* 8 | *.result.css 9 | /index.* 10 | node_modules 11 | package-lock.json 12 | -------------------------------------------------------------------------------- /.rollup.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | 3 | export default { 4 | input: 'src/index.js', 5 | output: [ 6 | { file: 'index.js', format: 'cjs', sourcemap: true }, 7 | { file: 'index.mjs', format: 'esm', sourcemap: true } 8 | ], 9 | plugins: [ 10 | babel({ 11 | presets: [ 12 | ['@babel/env', { modules: false, targets: { node: 6 } }] 13 | ] 14 | }) 15 | ] 16 | }; 17 | -------------------------------------------------------------------------------- /.tape.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'basic': { 3 | message: 'supports basic usage' 4 | }, 5 | 'basic:precision': { 6 | message: 'supports { precision } usage', 7 | options: { 8 | precision: 3 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://docs.travis-ci.com/user/travis-lint 2 | 3 | language: node_js 4 | 5 | node_js: 6 | - 6 7 | 8 | install: 9 | - npm install --ignore-scripts 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changes to PostCSS Trig 2 | 3 | ### 1.0.0 (February 28, 2019) 4 | 5 | - Initial version 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to PostCSS Trig 2 | 3 | You want to help? You rock! Now, take a moment to be sure your contributions 4 | make sense to everyone else. 5 | 6 | ## Reporting Issues 7 | 8 | Found a problem? Want a new feature? 9 | 10 | - See if your issue or idea has [already been reported]. 11 | - Provide a [reduced test case] or a [live example]. 12 | 13 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 14 | 15 | ## Submitting Pull Requests 16 | 17 | Pull requests are the greatest contributions, so be sure they are focused in 18 | scope and avoid unrelated commits. 19 | 20 | 1. To begin; [fork this project], clone your fork, and add our upstream. 21 | ```bash 22 | # Clone your fork of the repo into the current directory 23 | git clone git@github.com:YOUR_USER/postcss-trig.git 24 | 25 | # Navigate to the newly cloned directory 26 | cd postcss-trig 27 | 28 | # Assign the original repo to a remote called "upstream" 29 | git remote add upstream git@github.com:csstools/postcss-trig.git 30 | 31 | # Install the tools necessary for testing 32 | npm install 33 | ``` 34 | 35 | 2. Create a branch for your feature or fix: 36 | ```bash 37 | # Move into a new branch for your feature 38 | git checkout -b feature/thing 39 | ``` 40 | ```bash 41 | # Move into a new branch for your fix 42 | git checkout -b fix/something 43 | ``` 44 | 45 | 3. If your code follows our practices, then push your feature branch: 46 | ```bash 47 | # Test current code 48 | npm test 49 | ``` 50 | ```bash 51 | # Push the branch for your new feature 52 | git push origin feature/thing 53 | ``` 54 | ```bash 55 | # Or, push the branch for your update 56 | git push origin update/something 57 | ``` 58 | 59 | That’s it! Now [open a pull request] with a clear title and description. 60 | 61 | [already been reported]: issues 62 | [fork this project]: fork 63 | [live example]: https://codepen.io/pen 64 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 65 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 66 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Installing PostCSS Trig 2 | 3 | [PostCSS Trig] runs in all Node environments, with special instructions for: 4 | 5 | | [Node](#node) | [PostCSS CLI](#postcss-cli) | [Webpack](#webpack) | [Create React App](#create-react-app) | [Gulp](#gulp) | [Grunt](#grunt) | 6 | | --- | --- | --- | --- | --- | --- | 7 | 8 | ## Node 9 | 10 | Add [PostCSS Trig] to your project: 11 | 12 | ```bash 13 | npm install postcss-trig --save-dev 14 | ``` 15 | 16 | Use [PostCSS Trig] to process your CSS: 17 | 18 | ```js 19 | const postcssTrig = require('postcss-trig'); 20 | 21 | postcssTrig.process(YOUR_CSS /*, processOptions, pluginOptions */); 22 | ``` 23 | 24 | Or use it as a [PostCSS] plugin: 25 | 26 | ```js 27 | const postcss = require('postcss'); 28 | const postcssTrig = require('postcss-trig'); 29 | 30 | postcss([ 31 | postcssTrig(/* pluginOptions */) 32 | ]).process(YOUR_CSS /*, processOptions */); 33 | ``` 34 | 35 | ## PostCSS CLI 36 | 37 | Add [PostCSS CLI] to your project: 38 | 39 | ```bash 40 | npm install postcss-cli --save-dev 41 | ``` 42 | 43 | Use [PostCSS Trig] in your `postcss.config.js` configuration file: 44 | 45 | ```js 46 | const postcssTrig = require('postcss-trig'); 47 | 48 | module.exports = { 49 | plugins: [ 50 | postcssTrig(/* pluginOptions */) 51 | ] 52 | } 53 | ``` 54 | 55 | ## Webpack 56 | 57 | Add [PostCSS Loader] to your project: 58 | 59 | ```bash 60 | npm install postcss-loader --save-dev 61 | ``` 62 | 63 | Use [PostCSS Trig] in your Webpack configuration: 64 | 65 | ```js 66 | const postcssTrig = require('postcss-trig'); 67 | 68 | module.exports = { 69 | module: { 70 | rules: [ 71 | { 72 | test: /\.css$/, 73 | use: [ 74 | 'style-loader', 75 | { loader: 'css-loader', options: { importLoaders: 1 } }, 76 | { loader: 'postcss-loader', options: { 77 | ident: 'postcss', 78 | plugins: () => [ 79 | postcssTrig(/* pluginOptions */) 80 | ] 81 | } } 82 | ] 83 | } 84 | ] 85 | } 86 | } 87 | ``` 88 | 89 | ## Create React App 90 | 91 | Add [React App Rewired] and [React App Rewire PostCSS] to your project: 92 | 93 | ```bash 94 | npm install react-app-rewired react-app-rewire-postcss --save-dev 95 | ``` 96 | 97 | Use [React App Rewire PostCSS] and [PostCSS Trig] in your 98 | `config-overrides.js` file: 99 | 100 | ```js 101 | const reactAppRewirePostcss = require('react-app-rewire-postcss'); 102 | const postcssTrig = require('postcss-trig'); 103 | 104 | module.exports = config => reactAppRewirePostcss(config, { 105 | plugins: () => [ 106 | postcssTrig(/* pluginOptions */) 107 | ] 108 | }); 109 | ``` 110 | 111 | ## Gulp 112 | 113 | Add [Gulp PostCSS] to your project: 114 | 115 | ```bash 116 | npm install gulp-postcss --save-dev 117 | ``` 118 | 119 | Use [PostCSS Trig] in your Gulpfile: 120 | 121 | ```js 122 | const postcss = require('gulp-postcss'); 123 | const postcssTrig = require('postcss-trig'); 124 | 125 | gulp.task('css', () => gulp.src('./src/*.css').pipe( 126 | postcss([ 127 | postcssTrig(/* pluginOptions */) 128 | ]) 129 | ).pipe( 130 | gulp.dest('.') 131 | )); 132 | ``` 133 | 134 | ## Grunt 135 | 136 | Add [Grunt PostCSS] to your project: 137 | 138 | ```bash 139 | npm install grunt-postcss --save-dev 140 | ``` 141 | 142 | Use [PostCSS Trig] in your Gruntfile: 143 | 144 | ```js 145 | const postcssTrig = require('postcss-trig'); 146 | 147 | grunt.loadNpmTasks('grunt-postcss'); 148 | 149 | grunt.initConfig({ 150 | postcss: { 151 | options: { 152 | use: [ 153 | postcssTrig(/* pluginOptions */) 154 | ] 155 | }, 156 | dist: { 157 | src: '*.css' 158 | } 159 | } 160 | }); 161 | ``` 162 | 163 | [Gulp PostCSS]: https://github.com/postcss/gulp-postcss 164 | [Grunt PostCSS]: https://github.com/nDmitry/grunt-postcss 165 | [PostCSS]: https://github.com/postcss/postcss 166 | [PostCSS CLI]: https://github.com/postcss/postcss-cli 167 | [PostCSS Loader]: https://github.com/postcss/postcss-loader 168 | [PostCSS Trig]: https://github.com/csstools/postcss-trig 169 | [React App Rewire PostCSS]: https://github.com/csstools/react-app-rewire-postcss 170 | [React App Rewired]: https://github.com/timarney/react-app-rewired 171 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal 2 | 3 | ## Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an “owner”) of an original work of 8 | authorship and/or a database (each, a “Work”). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific works 12 | (“Commons”) that the public can reliably and without fear of later claims of 13 | infringement build upon, modify, incorporate in other works, reuse and 14 | redistribute as freely as possible in any form whatsoever and for any purposes, 15 | including without limitation commercial purposes. These owners may contribute 16 | to the Commons to promote the ideal of a free culture and the further 17 | production of creative, cultural and scientific works, or to gain reputation or 18 | greater distribution for their Work in part through the use and efforts of 19 | others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation of 22 | additional consideration or compensation, the person associating CC0 with a 23 | Work (the “Affirmer”), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and 25 | publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights (“Copyright and 31 | Related Rights”). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 1. the right to reproduce, adapt, distribute, perform, display, communicate, 34 | and translate a Work; 35 | 2. moral rights retained by the original author(s) and/or performer(s); 36 | 3. publicity and privacy rights pertaining to a person’s image or likeness 37 | depicted in a Work; 38 | 4. rights protecting against unfair competition in regards to a Work, 39 | subject to the limitations in paragraph 4(i), below; 40 | 5. rights protecting the extraction, dissemination, use and reuse of data in 41 | a Work; 42 | 6. database rights (such as those arising under Directive 96/9/EC of the 43 | European Parliament and of the Council of 11 March 1996 on the legal 44 | protection of databases, and under any national implementation thereof, 45 | including any amended or successor version of such directive); and 46 | 7. other similar, equivalent or corresponding rights throughout the world 47 | based on applicable law or treaty, and any national implementations 48 | thereof. 49 | 50 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 51 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 52 | unconditionally waives, abandons, and surrenders all of Affirmer’s Copyright 53 | and Related Rights and associated claims and causes of action, whether now 54 | known or unknown (including existing as well as future claims and causes of 55 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 56 | duration provided by applicable law or treaty (including future time 57 | extensions), (iii) in any current or future medium and for any number of 58 | copies, and (iv) for any purpose whatsoever, including without limitation 59 | commercial, advertising or promotional purposes (the “Waiver”). Affirmer 60 | makes the Waiver for the benefit of each member of the public at large and 61 | to the detriment of Affirmer’s heirs and successors, fully intending that 62 | such Waiver shall not be subject to revocation, rescission, cancellation, 63 | termination, or any other legal or equitable action to disrupt the quiet 64 | enjoyment of the Work by the public as contemplated by Affirmer’s express 65 | Statement of Purpose. 66 | 67 | 3. Public License Fallback. Should any part of the Waiver for any reason be 68 | judged legally invalid or ineffective under applicable law, then the Waiver 69 | shall be preserved to the maximum extent permitted taking into account 70 | Affirmer’s express Statement of Purpose. In addition, to the extent the 71 | Waiver is so judged Affirmer hereby grants to each affected person a 72 | royalty-free, non transferable, non sublicensable, non exclusive, 73 | irrevocable and unconditional license to exercise Affirmer’s Copyright and 74 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 75 | maximum duration provided by applicable law or treaty (including future time 76 | extensions), (iii) in any current or future medium and for any number of 77 | copies, and (iv) for any purpose whatsoever, including without limitation 78 | commercial, advertising or promotional purposes (the “License”). The License 79 | shall be deemed effective as of the date CC0 was applied by Affirmer to the 80 | Work. Should any part of the License for any reason be judged legally 81 | invalid or ineffective under applicable law, such partial invalidity or 82 | ineffectiveness shall not invalidate the remainder of the License, and in 83 | such case Affirmer hereby affirms that he or she will not (i) exercise any 84 | of his or her remaining Copyright and Related Rights in the Work or (ii) 85 | assert any associated claims and causes of action with respect to the Work, 86 | in either case contrary to Affirmer’s express Statement of Purpose. 87 | 88 | 4. Limitations and Disclaimers. 89 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, 90 | surrendered, licensed or otherwise affected by this document. 91 | 2. Affirmer offers the Work as-is and makes no representations or warranties 92 | of any kind concerning the Work, express, implied, statutory or 93 | otherwise, including without limitation warranties of title, 94 | merchantability, fitness for a particular purpose, non infringement, or 95 | the absence of latent or other defects, accuracy, or the present or 96 | absence of errors, whether or not discoverable, all to the greatest 97 | extent permissible under applicable law. 98 | 3. Affirmer disclaims responsibility for clearing rights of other persons 99 | that may apply to the Work or any use thereof, including without 100 | limitation any person’s Copyright and Related Rights in the Work. 101 | Further, Affirmer disclaims responsibility for obtaining any necessary 102 | consents, permissions or other rights required for any use of the Work. 103 | 4. Affirmer understands and acknowledges that Creative Commons is not a 104 | party to this document and has no duty or obligation with respect to this 105 | CC0 or use of the Work. 106 | 107 | For more information, please see 108 | http://creativecommons.org/publicdomain/zero/1.0/. 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
⚠️ PostCSS Trig is no longer maintained ⚠️
There's a new plugin that handles trigonometric which you can find in @csstools/postcss-plugins.
2 | 3 | # PostCSS Trig [PostCSS][postcss] 4 | 5 | [![NPM Version][npm-img]][npm-url] 6 | [![Build Status][cli-img]][cli-url] 7 | [![Support Chat][git-img]][git-url] 8 | 9 | [PostCSS Trig] lets you use trigonometry functions in CSS. 10 | 11 | These functions include `acos`, `asin`, `atan`, `atan2`, `cos`, `hypot`, `pow`, 12 | `sin`, `sqrt`, and `tan`. 13 | 14 | ```pcss 15 | :root { 16 | --acos: acos(0.8); 17 | --asin: asin(0.6); 18 | --atan: atan(0.8); 19 | --atan2: atan2(5, 5); 20 | --cos: cos(20); 21 | --cos-deg: cos(20deg); 22 | --hypot: hypot(3, 4); 23 | --pow: pow(4, 4); 24 | --pow-rem: pow(4rem, 4); 25 | --sin: sin(20); 26 | --sin-deg: sin(20deg); 27 | --sqrt: sqrt(81); 28 | --sqrt-rem: sqrt(81rem); 29 | --tan: tan(20); 30 | --tan-deg: tan(20deg); 31 | } 32 | 33 | 34 | /* becomes */ 35 | 36 | :root { 37 | --acos: 0.6435; 38 | --asin: 0.6435; 39 | --atan: 0.9273; 40 | --atan2: 0.7854; 41 | --cos: 0.40808; 42 | --cos-deg: 0.93969; 43 | --hypot: 5; 44 | --pow: 256; 45 | --pow-rem: 256rem; 46 | --sin: 0.91295; 47 | --sin-deg: 0.34202; 48 | --sqrt: 9; 49 | --sqrt-rem: 9rem; 50 | --tan: 2.23716; 51 | --tan-deg: 0.36397; 52 | } 53 | ``` 54 | 55 | When necessary, more complex fallbacks are used to support real-time values. 56 | 57 | ```pcss 58 | :root { 59 | --cos-calc: cos(calc(20 * 2)); 60 | --cos-var: cos(var(--angle)); 61 | --pow-calc: pow(calc(4 * 2), 4); 62 | --pow-var: pow(var(--number), 4); 63 | --sin-calc: sin(calc(20 * 2)); 64 | --sin-var: sin(var(--angle)); 65 | --sqrt-calc: sqrt(calc(81 * 4)); 66 | --sqrt-var: sqrt(var(--number)); 67 | --tan-calc: tan(calc(20 * 2)); 68 | --tan-var: tan(var(--angle)); 69 | } 70 | 71 | /* becomes */ 72 | 73 | :root { 74 | --cos-calc: calc(1 - ((20 * 2) * (20 * 2) / 2 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 24 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 720 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 40320 )); 75 | --cos-var: calc(1 - (var(--angle) * var(--angle) / 2 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) / 24 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 720 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 40320 )); 76 | --pow-calc: pow(calc(4 * 2), 4); 77 | --pow-var: pow(var(--number), 4); 78 | --sin-calc: calc((20 * 2) - ((20 * 2) * (20 * 2) * (20 * 2) / 6 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 120 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 5040 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 362880 )); 79 | --sin-var: calc(var(--angle) - (var(--angle) * var(--angle) * var(--angle) / 6 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 120 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 5040 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 362880 )); 80 | --sqrt-calc: calc(((((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2) + (81 * 4) / (((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2)) / 2); 81 | --sqrt-var: calc((((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2) + var(--number) / ((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2)) / 2); 82 | --tan-calc: calc((20 * 2) + ((1 / 3) * ((20 * 2) * (20 * 2) * (20 * 2))) + ((2 / 15) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2))) + ((17 / 315) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2)))); 83 | --tan-var: calc(var(--angle) + ((1 / 3) * (var(--angle) * var(--angle) * var(--angle))) + ((2 / 15) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle))) + ((17 / 315) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)))); 84 | } 85 | ``` 86 | 87 | ## Usage 88 | 89 | Add [PostCSS Trig] to your project: 90 | 91 | ```bash 92 | npm install postcss-trig --save-dev 93 | ``` 94 | 95 | Use [PostCSS Trig] to process your CSS: 96 | 97 | ```js 98 | const postcssTrig = require('postcss-trig'); 99 | 100 | postcssTrig.process(YOUR_CSS /*, processOptions, pluginOptions */); 101 | ``` 102 | 103 | Or use it as a [PostCSS] plugin: 104 | 105 | ```js 106 | const postcss = require('postcss'); 107 | const postcssTrig = require('postcss-trig'); 108 | 109 | postcss([ 110 | postcssTrig(/* pluginOptions */) 111 | ]).process(YOUR_CSS /*, processOptions */); 112 | ``` 113 | 114 | [PostCSS Trig] runs in all Node environments, with special instructions for: 115 | 116 | | [Node](INSTALL.md#node) | [PostCSS CLI](INSTALL.md#postcss-cli) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) | 117 | | --- | --- | --- | --- | --- | --- | 118 | 119 | ## Options 120 | 121 | ### precision 122 | 123 | The `precision` option determines how many decimal places of precision will be 124 | preserved after transformations. By default, this precision is up to `5` places. 125 | 126 | [cli-img]: https://img.shields.io/travis/csstools/postcss-trig.svg 127 | [cli-url]: https://travis-ci.org/csstools/postcss-trig 128 | [git-img]: https://img.shields.io/badge/support-chat-blue.svg 129 | [git-url]: https://gitter.im/postcss/postcss 130 | [npm-img]: https://img.shields.io/npm/v/postcss-trig.svg 131 | [npm-url]: https://www.npmjs.com/package/postcss-trig 132 | 133 | [PostCSS]: https://github.com/postcss/postcss 134 | [PostCSS Trig]: https://github.com/csstools/postcss-trig 135 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postcss-trig", 3 | "version": "1.0.0", 4 | "description": "Use trigonometry functions in CSS", 5 | "author": "Jonathan Neal ", 6 | "license": "CC0-1.0", 7 | "repository": "csstools/postcss-trig", 8 | "homepage": "https://github.com/csstools/postcss-trig#readme", 9 | "bugs": "https://github.com/csstools/postcss-trig/issues", 10 | "main": "index.js", 11 | "module": "index.mjs", 12 | "files": [ 13 | "index.js", 14 | "index.js.map", 15 | "index.mjs", 16 | "index.mjs.map" 17 | ], 18 | "scripts": { 19 | "build": "rollup --config .rollup.js --silent", 20 | "prepublishOnly": "npm test", 21 | "pretest:tape": "npm run build", 22 | "test": "npm run test:js && npm run test:tape", 23 | "test:js": "eslint src/*.js src/lib/*.js --cache --ignore-path .gitignore --quiet", 24 | "test:tape": "postcss-tape" 25 | }, 26 | "engines": { 27 | "node": ">=6.0.0" 28 | }, 29 | "dependencies": { 30 | "postcss": "^7.0.14", 31 | "postcss-values-parser": "^2.0.1" 32 | }, 33 | "devDependencies": { 34 | "@babel/core": "^7.2.2", 35 | "@babel/preset-env": "^7.3.1", 36 | "babel-eslint": "^10.0.1", 37 | "eslint": "^5.12.1", 38 | "eslint-config-dev": "^2.0.0", 39 | "postcss-tape": "^4.0.0", 40 | "pre-commit": "^1.2.2", 41 | "rollup": "^1.1.2", 42 | "rollup-plugin-babel": "^4.3.2" 43 | }, 44 | "eslintConfig": { 45 | "extends": "dev", 46 | "parser": "babel-eslint" 47 | }, 48 | "keywords": [ 49 | "postcss", 50 | "css", 51 | "postcss-plugin", 52 | "trigonometry", 53 | "math", 54 | "calc" 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import postcss from 'postcss'; 2 | import valuesParser from 'postcss-values-parser'; 3 | import { 4 | transformAcosFunction, 5 | transformAsinFunction, 6 | transformAtanFunction, 7 | transformAtan2Function, 8 | transformCosFunction, 9 | transformHypotFunction, 10 | transformPowFunction, 11 | transformSinFunction, 12 | transformSqrtFunction, 13 | transformTanFunction 14 | } from './lib/transforms'; 15 | 16 | export default postcss.plugin('postcss-trig', opts => { 17 | const precision = 'precision' in Object(opts) ? Number(opts.precision) || 0 : 5; 18 | 19 | return root => { 20 | root.walkDecls(decl => { 21 | if (anyTrigFunctionRegExp.test(decl.value)) { 22 | const originalValue = decl.value; 23 | 24 | const valueAST = valuesParser(originalValue).parse(); 25 | 26 | valueAST.walkFunctionNodes(functionNode => { 27 | if (trigFunctionNameRegExp.test(functionNode.value)) { 28 | const name = functionNode.value.toLowerCase(); 29 | const transform = transformsByName[name]; 30 | 31 | transform(functionNode, precision); 32 | } 33 | }); 34 | 35 | const modifiedValue = String(valueAST); 36 | 37 | if (originalValue !== modifiedValue) { 38 | decl.value = modifiedValue; 39 | } 40 | } 41 | }); 42 | }; 43 | }); 44 | 45 | const transformsByName = { 46 | acos: transformAcosFunction, 47 | asin: transformAsinFunction, 48 | atan: transformAtanFunction, 49 | atan2: transformAtan2Function, 50 | cos: transformCosFunction, 51 | hypot: transformHypotFunction, 52 | pow: transformPowFunction, 53 | sin: transformSinFunction, 54 | sqrt: transformSqrtFunction, 55 | tan: transformTanFunction 56 | }; 57 | 58 | const anyTrigFunctionRegExp = /\b(acos|asin|atan|atan2|cos|hypot|pow|sin|sqrt|tan)\(.*\)/i; 59 | const trigFunctionNameRegExp = /^(acos|asin|atan|atan2|cos|hypot|pow|sin|sqrt|tan)$/i 60 | -------------------------------------------------------------------------------- /src/lib/is.js: -------------------------------------------------------------------------------- 1 | /* Numbers 2 | /* ========================================================================== */ 3 | 4 | // return whether the length is valid 5 | export function isLength(node) { 6 | return Object(node).type === 'number'; 7 | } 8 | 9 | // return whether the length is valid 10 | export function isAngle(node) { 11 | return isLength(node) && matchAngleUnit.test(node.unit); 12 | } 13 | 14 | // return whether the number is valid 15 | export function isNumber(node) { 16 | return isLength(node) && node.unit === ''; 17 | } 18 | 19 | /* Delimiters 20 | /* ========================================================================== */ 21 | 22 | // return whether the comma is valid 23 | export function isComma(node) { 24 | return Object(node).type === 'comma'; 25 | } 26 | 27 | /* Functions 28 | /* ========================================================================== */ 29 | 30 | export function isFunction(node) { 31 | return Object(node).type === 'func'; 32 | } 33 | 34 | export function isCalc(node) { 35 | return isFunction(node) && matchCalcName.test(node.value); 36 | } 37 | 38 | /* Matchers 39 | /* ========================================================================== */ 40 | 41 | export const matchAngleUnit = /^(deg|grad|rad|turn)$/i; 42 | export const matchCalcName = /^calc$/i; 43 | -------------------------------------------------------------------------------- /src/lib/new.js: -------------------------------------------------------------------------------- 1 | import valuesParser from 'postcss-values-parser'; 2 | 3 | export function newNumber(value) { 4 | return valuesParser.number({ value: String(value) }); 5 | } 6 | 7 | export function newOperator(value) { 8 | const multiplierOpts = { value, raws: { before: ' ', after: ' ' } }; 9 | 10 | return valuesParser.operator(multiplierOpts); 11 | } 12 | 13 | export function newFunction(name, ...nodes) { 14 | return valuesParser.func({ 15 | value: name, 16 | nodes: [ 17 | valuesParser.paren({ value: '(' }), 18 | ...nodes, 19 | valuesParser.paren({ value: ')' }) 20 | ] 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/transforms.js: -------------------------------------------------------------------------------- 1 | import { isAngle, isCalc, isComma, isFunction, isLength, isNumber, matchCalcName } from './is'; 2 | import { newNumber, newOperator, newFunction } from './new'; 3 | 4 | /* Transforms 5 | /* ========================================================================== */ 6 | 7 | // return the first set of transformed arguments allowable by the parameters 8 | export function transformArgsByParams(node, params) { 9 | const nodes = (Object(node).nodes || []).slice(1, -1); 10 | 11 | return params.map(param => nodes.map( 12 | (childNode, index) => typeof param[index] === 'function' ? param[index](childNode) : null 13 | ).filter(child => typeof child !== 'boolean')).filter(param => param.every( 14 | result => result !== null 15 | ))[0] || []; 16 | } 17 | 18 | // return a transformed angle 19 | export function transformAngleOrNumberNodeIntoNumberNode(node) { 20 | if (isAngle(node) || isNumber(node)) { 21 | const unit = node.unit.toLowerCase(); 22 | 23 | if (unit in angleMultipliersByUnit) { 24 | node.value = String(Number(node.value) * angleMultipliersByUnit[unit]); 25 | 26 | node.unit = ''; 27 | } 28 | 29 | return node; 30 | } 31 | 32 | return null; 33 | } 34 | 35 | function transformCalcAddDivideBy(node, number) { 36 | if (isCalc(node)) { 37 | node.nodes.splice(-1, 0, newOperator('/'), newOperator(number)); 38 | } 39 | 40 | return node; 41 | } 42 | 43 | export function getFunctionNode(node) { 44 | return isFunction(node) ? node : null; 45 | } 46 | 47 | export function getLengthNode(node) { 48 | return isLength(node) ? node : null; 49 | } 50 | 51 | export function getNumber(node) { 52 | return isNumber(node) ? Number(node.value) : null; 53 | } 54 | 55 | export function getNumberNode(node) { 56 | return isNumber(node) ? node : null; 57 | } 58 | 59 | export function uncalcNode(node) { 60 | if (isCalc(node)) { 61 | node.value = node.value.replace(matchCalcName, ''); 62 | } 63 | 64 | return node; 65 | } 66 | 67 | /* Transform acos() 68 | /* ========================================================================== */ 69 | 70 | export function transformAcosFunction(functionNode, precision) { 71 | const [radianNode] = transformArgsByParams(functionNode, [ 72 | // | 73 | [transformAngleOrNumberNodeIntoNumberNode] 74 | ]); 75 | 76 | if (isNumber(radianNode)) { 77 | const numberNode = getAcosNumberNodeFromNumberNode(radianNode, precision); 78 | 79 | if (numberNode.value !== 'NaN') { 80 | functionNode.replaceWith(numberNode); 81 | } 82 | } 83 | } 84 | 85 | export function getAcosNumberNodeFromNumberNode(numberNode, precision) { 86 | const acos = usePrecision(Math.acos(numberNode.value), precision); 87 | 88 | return clone(numberNode, { value: String(acos) }); 89 | } 90 | 91 | /* Transform asin() 92 | /* ========================================================================== */ 93 | 94 | export function transformAsinFunction(functionNode, precision) { 95 | const [radianNode] = transformArgsByParams(functionNode, [ 96 | // | 97 | [transformAngleOrNumberNodeIntoNumberNode] 98 | ]); 99 | 100 | if (isNumber(radianNode)) { 101 | const numberNode = getAsinNumberNodeFromNumberNode(radianNode, precision); 102 | 103 | if (numberNode.value !== 'NaN') { 104 | functionNode.replaceWith(numberNode); 105 | } 106 | } 107 | } 108 | 109 | export function getAsinNumberNodeFromNumberNode(numberNode, precision) { 110 | const asin = usePrecision(Math.asin(numberNode.value), precision); 111 | 112 | return clone(numberNode, { value: String(asin) }); 113 | } 114 | 115 | /* Transform asin() 116 | /* ========================================================================== */ 117 | 118 | export function transformAtanFunction(functionNode, precision) { 119 | const [radianNode] = transformArgsByParams(functionNode, [ 120 | // | 121 | [transformAngleOrNumberNodeIntoNumberNode] 122 | ]); 123 | 124 | if (isNumber(radianNode)) { 125 | const numberNode = getAtanNumberNodeFromNumberNode(radianNode, precision); 126 | 127 | if (numberNode.value !== 'NaN') { 128 | functionNode.replaceWith(numberNode); 129 | } 130 | } 131 | } 132 | 133 | export function getAtanNumberNodeFromNumberNode(numberNode, precision) { 134 | const atan = usePrecision(Math.asin(numberNode.value), precision); 135 | 136 | return clone(numberNode, { value: String(atan) }); 137 | } 138 | 139 | /* Transform atan2() 140 | /* ========================================================================== */ 141 | 142 | export function transformAtan2Function(functionNode, precision) { 143 | const [coordinateY, coordinateX] = transformArgsByParams(functionNode, [ 144 | // , 145 | [getNumber, isComma, getNumber] 146 | ]); 147 | 148 | if (typeof coordinateY === 'number' && typeof coordinateX === 'number') { 149 | const numberNode = getAtan2NumberNodeFromCoordinates(coordinateY, coordinateX, precision); 150 | 151 | if (numberNode.value !== 'NaN') { 152 | functionNode.replaceWith(numberNode); 153 | } 154 | } 155 | } 156 | 157 | export function getAtan2NumberNodeFromCoordinates(coordinateY, coordinateX, precision) { 158 | const atan2 = usePrecision(Math.atan2(coordinateY, coordinateX), precision); 159 | 160 | return newNumber(atan2); 161 | } 162 | 163 | /* Transform cos() 164 | /* ========================================================================== */ 165 | 166 | export function transformCosFunction(cosFunctionNode, precision) { 167 | const [radianNode] = transformArgsByParams(cosFunctionNode, [ 168 | // | 169 | [transformAngleOrNumberNodeIntoNumberNode], 170 | // 171 | [getFunctionNode] 172 | ]); 173 | 174 | if (isNumber(radianNode)) { 175 | const numberNode = getCosNumberNodeFromNumberNode(radianNode, precision); 176 | 177 | if (numberNode.value !== 'NaN') { 178 | cosFunctionNode.replaceWith(numberNode); 179 | } 180 | } else if (isFunction(radianNode)) { 181 | cosFunctionNode.replaceWith(getCosFunctionNodeFromFunctionNode(radianNode)); 182 | } 183 | } 184 | 185 | export function getCosNumberNodeFromNumberNode(numberNode, precision) { 186 | const cos = usePrecision(Math.cos(numberNode.value), precision); 187 | 188 | return clone(numberNode, { value: String(cos) }); 189 | } 190 | 191 | export function getCosFunctionNodeFromFunctionNode(functionNode) { 192 | const cosTermNode1 = newNumber(1); 193 | const cosTermNode2 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 2), 2); 194 | const cosTermNode3 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 4), 24); 195 | const cosTermNode4 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 6), 720); 196 | const cosTermNode5 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 8), 40320); 197 | const calcCosNode = newFunction('calc', ...[ 198 | uncalcNode(cosTermNode1), 199 | newOperator('-'), 200 | uncalcNode(cosTermNode2), 201 | newOperator('+'), 202 | uncalcNode(cosTermNode3), 203 | newOperator('-'), 204 | uncalcNode(cosTermNode4), 205 | newOperator('+'), 206 | uncalcNode(cosTermNode5) 207 | ]); 208 | 209 | return calcCosNode; 210 | } 211 | 212 | /* Transform hypot() 213 | /* ========================================================================== */ 214 | 215 | export function transformHypotFunction(hypotFunctionNode, precision) { 216 | const hypotNodes = hypotFunctionNode.nodes.slice(1, -1); 217 | 218 | const isValidHypotArgs = hypotNodes.every((hypotNode, index) => index % 2 ? isComma(hypotNode) : isNumber(hypotNode)); 219 | 220 | if (isValidHypotArgs) { 221 | const numberNode = getHypotNumberNodeFromHypotNodes(hypotNodes, precision); 222 | 223 | if (numberNode.value !== 'NaN') { 224 | hypotFunctionNode.replaceWith(numberNode); 225 | } 226 | } 227 | } 228 | 229 | export function getHypotNumberNodeFromHypotNodes(hypotNodes, precision) { 230 | const hypotNodesValues = hypotNodes.filter((hypotNode, index) => !(index % 2)).map(hypotNode => hypotNode.value); 231 | const hypot = usePrecision(Math.hypot.apply(Math, hypotNodesValues), precision); 232 | const hypotNode = newNumber(hypot); 233 | 234 | return hypotNode; 235 | } 236 | 237 | /* Transform pow() 238 | /* ========================================================================== */ 239 | 240 | export function transformPowFunction(powFunctionNode, precision) { 241 | const [baseNode, exponent] = transformArgsByParams(powFunctionNode, [ 242 | // , 243 | [getLengthNode, isComma, getNumber], 244 | // , 245 | [getFunctionNode, isComma, getNumber] 246 | ]); 247 | 248 | if (isLength(baseNode) && typeof exponent === 'number') { 249 | const lengthNode = getPowLengthNodeFromLengthNode(baseNode, exponent, precision); 250 | 251 | if (lengthNode.value !== 'NaN') { 252 | powFunctionNode.replaceWith(lengthNode); 253 | } 254 | } else if (isFunction(baseNode) && typeof exponentNumber === 'number') { 255 | powFunctionNode.replaceWith(getPowCalcFunctionNodeFromFunctionNode(baseNode, exponent)); 256 | } 257 | } 258 | 259 | export function getPowLengthNodeFromLengthNode(lengthNode, exponent, precision) { 260 | const base = Number(lengthNode.value); 261 | const pow = usePrecision(Math.pow(base, exponent), precision); 262 | 263 | return clone(lengthNode, { value: String(pow) }); 264 | } 265 | 266 | export function getPowCalcFunctionNodeFromFunctionNode(functionNode, exponent) { 267 | const functionCloneNode = uncalcNode(clone(functionNode)); 268 | const calcSumNodes = []; 269 | let i = -1; 270 | 271 | while (++i < exponent) { 272 | calcSumNodes.push(clone(functionCloneNode)); 273 | 274 | if (i + 1 < exponent) { 275 | calcSumNodes.push(newOperator('*')); 276 | } 277 | } 278 | 279 | return newFunction('calc', ...calcSumNodes); 280 | } 281 | 282 | /* Transform sin() 283 | /* ========================================================================== */ 284 | 285 | export function transformSinFunction(sinFunctionNode, precision) { 286 | const [radianNode] = transformArgsByParams(sinFunctionNode, [ 287 | // | 288 | [transformAngleOrNumberNodeIntoNumberNode], 289 | // 290 | [getFunctionNode] 291 | ]); 292 | 293 | if (isNumber(radianNode)) { 294 | const numberNode = getSinNumberNodeFromNumberNode(radianNode, precision); 295 | 296 | if (numberNode.value !== 'NaN') { 297 | sinFunctionNode.replaceWith(numberNode); 298 | } 299 | } else if (isFunction(radianNode)) { 300 | sinFunctionNode.replaceWith(getSinCalcFunctionNodeFromFunctionNode(radianNode)); 301 | } 302 | } 303 | 304 | export function getSinNumberNodeFromNumberNode(numberNode, precision) { 305 | const sin = usePrecision(Math.sin(numberNode.value), precision); 306 | 307 | return clone(numberNode, { value: String(sin) }); 308 | } 309 | 310 | export function getSinCalcFunctionNodeFromFunctionNode(functionNode) { 311 | const sinTermNode1 = clone(functionNode); 312 | const sinTermNode2 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 3), 6); 313 | const sinTermNode3 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 5), 120); 314 | const sinTermNode4 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 7), 5040); 315 | const sinTermNode5 = transformCalcAddDivideBy(getPowCalcFunctionNodeFromFunctionNode(functionNode, 9), 362880); 316 | const calcSinNode = newFunction('calc', ...[ 317 | uncalcNode(sinTermNode1), 318 | newOperator('-'), 319 | uncalcNode(sinTermNode2), 320 | newOperator('+'), 321 | uncalcNode(sinTermNode3), 322 | newOperator('-'), 323 | uncalcNode(sinTermNode4), 324 | newOperator('+'), 325 | uncalcNode(sinTermNode5) 326 | ]); 327 | 328 | return calcSinNode; 329 | } 330 | 331 | /* Transform sqrt() 332 | /* ========================================================================== */ 333 | 334 | export function transformSqrtFunction(sqrtFunctionNode, precision) { 335 | const [baseNode] = transformArgsByParams(sqrtFunctionNode, [ 336 | // , 337 | [getLengthNode], 338 | // , 339 | [getFunctionNode] 340 | ]); 341 | 342 | if (isLength(baseNode)) { 343 | const sqrtNumberNode = getSqrtLengthNodeFromLengthNode(baseNode, precision); 344 | 345 | if (sqrtNumberNode.value !== 'NaN') { 346 | sqrtFunctionNode.replaceWith(sqrtNumberNode); 347 | } 348 | } else if (isFunction(baseNode)) { 349 | sqrtFunctionNode.replaceWith(getSqrtCalcFunctionNodeFromFunctionNode(baseNode)); 350 | } 351 | } 352 | 353 | export function getSqrtLengthNodeFromLengthNode(lengthNode, precision) { 354 | const sqrt = usePrecision(Math.sqrt(lengthNode.value), precision); 355 | 356 | return clone(lengthNode, { value: String(sqrt) }); 357 | } 358 | 359 | export function getSqrtCalcFunctionNodeFromFunctionNode(functionNode) { 360 | const sqrtGuess1 = getSqrtGuessCalcFunctionNodeFromFunctionNode(functionNode, functionNode); 361 | const sqrtGuess2 = getSqrtGuessCalcFunctionNodeFromFunctionNode(sqrtGuess1, functionNode); 362 | const sqrtGuess3 = getSqrtGuessCalcFunctionNodeFromFunctionNode(sqrtGuess2, functionNode); 363 | const sqrtGuess4 = getSqrtGuessCalcFunctionNodeFromFunctionNode(sqrtGuess3, functionNode); 364 | const sqrtGuess5 = getSqrtGuessCalcFunctionNodeFromFunctionNode(sqrtGuess4, functionNode); 365 | 366 | return sqrtGuess5; 367 | } 368 | 369 | export function getSqrtGuessCalcFunctionNodeFromFunctionNode(guessFunctionNode, originalFunctionNode) { 370 | return newFunction('calc', ...[ 371 | newFunction('', ...[ 372 | ...[ 373 | uncalcNode(clone(guessFunctionNode)), 374 | newOperator('+'), 375 | uncalcNode(clone(originalFunctionNode)), 376 | newOperator('/'), 377 | uncalcNode(clone(guessFunctionNode)) 378 | ] 379 | ]), 380 | newOperator('/'), 381 | newNumber(2) 382 | ]); 383 | } 384 | 385 | /* Transform tan() 386 | /* ========================================================================== */ 387 | 388 | export function transformTanFunction(tanFunctionNode, precision) { 389 | const [radianNode] = transformArgsByParams(tanFunctionNode, [ 390 | // | 391 | [transformAngleOrNumberNodeIntoNumberNode], 392 | // 393 | [getFunctionNode] 394 | ]); 395 | 396 | if (isNumber(radianNode)) { 397 | const numberNode = getTanNumberNodeFromNumberNode(radianNode, precision); 398 | 399 | if (numberNode.value !== 'NaN') { 400 | tanFunctionNode.replaceWith(numberNode); 401 | } 402 | } else if (isFunction(radianNode)) { 403 | tanFunctionNode.replaceWith(getTanCalcFunctionNodeFromFunctionNode(radianNode)); 404 | } 405 | } 406 | 407 | export function getTanNumberNodeFromNumberNode(numberNode, precision) { 408 | const tan = usePrecision(Math.tan(numberNode.value), precision); 409 | 410 | return clone(numberNode, { value: String(tan) }); 411 | } 412 | 413 | export function getTanCalcFunctionNodeFromFunctionNode(functionNode) { 414 | const tanTermNode1 = clone(functionNode); 415 | const tanTermNode2 = newFunction('calc', ...[ 416 | newFunction('', ...[ 417 | newNumber(1), newOperator('/'), newNumber(3) 418 | ]), 419 | newOperator('*'), 420 | ...getPowCalcFunctionNodeFromFunctionNode(functionNode, 3).nodes 421 | ]); 422 | const tanTermNode3 = newFunction('calc', ...[ 423 | newFunction('', ...[ 424 | newNumber(2), newOperator('/'), newNumber(15) 425 | ]), 426 | newOperator('*'), 427 | ...getPowCalcFunctionNodeFromFunctionNode(functionNode, 5).nodes 428 | ]); 429 | const tanTermNode4 = newFunction('calc', ...[ 430 | newFunction('', ...[ 431 | newNumber(17), newOperator('/'), newNumber(315) 432 | ]), 433 | newOperator('*'), 434 | ...getPowCalcFunctionNodeFromFunctionNode(functionNode, 7).nodes 435 | ]); 436 | const calcTanNode = newFunction('calc', ...[ 437 | uncalcNode(tanTermNode1), 438 | newOperator('+'), 439 | uncalcNode(tanTermNode2), 440 | newOperator('+'), 441 | uncalcNode(tanTermNode3), 442 | newOperator('+'), 443 | uncalcNode(tanTermNode4) 444 | ]); 445 | 446 | return calcTanNode; 447 | } 448 | 449 | /* Conversions 450 | /* ========================================================================== */ 451 | 452 | const angleMultipliersByUnit = { 453 | '': 1, 454 | deg: Math.PI / 180, 455 | grad: Math.PI / 200, 456 | rad: 1, 457 | turn: 2 * Math.PI 458 | }; 459 | 460 | /* Clone 461 | /* ========================================================================== */ 462 | 463 | function clone(node, overrides) { 464 | return normalize(node, node.clone(overrides)); 465 | } 466 | 467 | function normalize(node, cloneNode) { 468 | if (node.raws) { 469 | for (const raw in node.raws) { 470 | cloneNode.raws[raw] = node.raws[raw]; 471 | } 472 | } 473 | 474 | if (node.nodes) { 475 | for (const index in node.nodes) { 476 | normalize(node.nodes[index], cloneNode.nodes[index]); 477 | } 478 | } 479 | 480 | return cloneNode; 481 | } 482 | 483 | function usePrecision(number, precision) { 484 | return Number(`${Math.round(`${number}e${precision}`)}e-${precision}`); 485 | } 486 | -------------------------------------------------------------------------------- /test/basic.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --acos: acos(0.8); 3 | --asin: asin(0.6); 4 | --atan: atan(0.8); 5 | --atan2: atan2(5, 5); 6 | --cos: cos(20); 7 | --cos-deg: cos(20deg); 8 | --hypot: hypot(3, 4); 9 | --pow: pow(4, 4); 10 | --pow-rem: pow(4rem, 4); 11 | --sin: sin(20); 12 | --sin-deg: sin(20deg); 13 | --sqrt: sqrt(81); 14 | --sqrt-rem: sqrt(81rem); 15 | --tan: tan(20); 16 | --tan-deg: tan(20deg); 17 | } 18 | 19 | :root { 20 | --cos-calc: cos(calc(20 * 2)); 21 | --cos-var: cos(var(--angle)); 22 | --pow-calc: pow(calc(4 * 2), 4); 23 | --pow-var: pow(var(--number), 4); 24 | --sin-calc: sin(calc(20 * 2)); 25 | --sin-var: sin(var(--angle)); 26 | --sqrt-calc: sqrt(calc(81 * 4)); 27 | --sqrt-var: sqrt(var(--number)); 28 | --tan-calc: tan(calc(20 * 2)); 29 | --tan-var: tan(var(--angle)); 30 | } 31 | -------------------------------------------------------------------------------- /test/basic.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --acos: 0.6435; 3 | --asin: 0.6435; 4 | --atan: 0.9273; 5 | --atan2: 0.7854; 6 | --cos: 0.40808; 7 | --cos-deg: 0.93969; 8 | --hypot: 5; 9 | --pow: 256; 10 | --pow-rem: 256rem; 11 | --sin: 0.91295; 12 | --sin-deg: 0.34202; 13 | --sqrt: 9; 14 | --sqrt-rem: 9rem; 15 | --tan: 2.23716; 16 | --tan-deg: 0.36397; 17 | } 18 | 19 | :root { 20 | --cos-calc: calc(1 - ((20 * 2) * (20 * 2) / 2 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 24 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 720 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 40320 )); 21 | --cos-var: calc(1 - (var(--angle) * var(--angle) / 2 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) / 24 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 720 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 40320 )); 22 | --pow-calc: pow(calc(4 * 2), 4); 23 | --pow-var: pow(var(--number), 4); 24 | --sin-calc: calc((20 * 2) - ((20 * 2) * (20 * 2) * (20 * 2) / 6 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 120 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 5040 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 362880 )); 25 | --sin-var: calc(var(--angle) - (var(--angle) * var(--angle) * var(--angle) / 6 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 120 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 5040 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 362880 )); 26 | --sqrt-calc: calc(((((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2) + (81 * 4) / (((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2)) / 2); 27 | --sqrt-var: calc((((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2) + var(--number) / ((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2)) / 2); 28 | --tan-calc: calc((20 * 2) + ((1 / 3) * ((20 * 2) * (20 * 2) * (20 * 2))) + ((2 / 15) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2))) + ((17 / 315) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2)))); 29 | --tan-var: calc(var(--angle) + ((1 / 3) * (var(--angle) * var(--angle) * var(--angle))) + ((2 / 15) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle))) + ((17 / 315) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)))); 30 | } 31 | -------------------------------------------------------------------------------- /test/basic.precision.expect.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --acos: 0.644; 3 | --asin: 0.644; 4 | --atan: 0.927; 5 | --atan2: 0.785; 6 | --cos: 0.408; 7 | --cos-deg: 0.94; 8 | --hypot: 5; 9 | --pow: 256; 10 | --pow-rem: 256rem; 11 | --sin: 0.913; 12 | --sin-deg: 0.342; 13 | --sqrt: 9; 14 | --sqrt-rem: 9rem; 15 | --tan: 2.237; 16 | --tan-deg: 0.364; 17 | } 18 | 19 | :root { 20 | --cos-calc: calc(1 - ((20 * 2) * (20 * 2) / 2 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 24 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 720 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 40320 )); 21 | --cos-var: calc(1 - (var(--angle) * var(--angle) / 2 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) / 24 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 720 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 40320 )); 22 | --pow-calc: pow(calc(4 * 2), 4); 23 | --pow-var: pow(var(--number), 4); 24 | --sin-calc: calc((20 * 2) - ((20 * 2) * (20 * 2) * (20 * 2) / 6 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 120 ) - ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 5040 ) + ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) / 362880 )); 25 | --sin-var: calc(var(--angle) - (var(--angle) * var(--angle) * var(--angle) / 6 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 120 ) - (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 5040 ) + (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) / 362880 )); 26 | --sqrt-calc: calc(((((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2) + (81 * 4) / (((((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2) + (81 * 4) / (((((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2) + (81 * 4) / (((((81 * 4) + (81 * 4) / (81 * 4)) / 2) + (81 * 4) / (((81 * 4) + (81 * 4) / (81 * 4)) / 2)) / 2)) / 2)) / 2)) / 2); 27 | --sqrt-var: calc((((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2) + var(--number) / ((((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2) + var(--number) / ((((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2) + var(--number) / ((((var(--number) + var(--number) / var(--number)) / 2) + var(--number) / ((var(--number) + var(--number) / var(--number)) / 2)) / 2)) / 2)) / 2)) / 2); 28 | --tan-calc: calc((20 * 2) + ((1 / 3) * ((20 * 2) * (20 * 2) * (20 * 2))) + ((2 / 15) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2))) + ((17 / 315) * ((20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2) * (20 * 2)))); 29 | --tan-var: calc(var(--angle) + ((1 / 3) * (var(--angle) * var(--angle) * var(--angle))) + ((2 / 15) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle))) + ((17 / 315) * (var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle) * var(--angle)))); 30 | } 31 | --------------------------------------------------------------------------------