├── .gitignore ├── .github └── FUNDING.yml ├── js ├── index.js~ ├── crossy.js ├── css.rotate.js ├── index.js ├── svg.select.min.js ├── svg.resize.min.js └── svg.draggy.js ├── LICENSE ├── package.json ├── css └── style.css ├── CONTRIBUTING.md ├── index.html └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | *.log 5 | node_modules 6 | *.env 7 | .DS_Store 8 | package-lock.json 9 | .bloggify/* 10 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ionicabizau 2 | patreon: ionicabizau 3 | open_collective: ionicabizau 4 | custom: https://www.buymeacoffee.com/h96wwchmy -------------------------------------------------------------------------------- /js/index.js~: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", function () { 2 | var t = new SVG(document.querySelector(".graph")).size("100%", 800) 3 | , elements = t.group().id("elements") 4 | , shapes = [ 5 | elements.group() 6 | , elements.group() 7 | , elements.group() 8 | , elements.group() 9 | , elements.group() 10 | , elements.group() 11 | , elements.group() 12 | ] 13 | ; 14 | 15 | // 1. Big Triangle 16 | shapes[0].polygon("0,0 200,200 400,0").fill("#e74c3c"); 17 | 18 | // 2. Big Triangle 19 | shapes[1].polygon("0,0 200,200 0,400").fill("#e67e22"); 20 | 21 | // 3. Medium Triangle 22 | shapes[2].polygon("400,400 200,400 400,200").fill("#f1c40f"); 23 | 24 | // 4. Small Triangle 25 | shapes[3].polygon("400,0 300,100 400,200").fill("#2ecc71"); 26 | 27 | // 5. Small Triangle 28 | shapes[4].polygon("200,200 100,300 300,300").fill("#3498db"); 29 | 30 | // 6. Square 31 | shapes[5].polygon("200,200 300,300 400,200 300,100").fill("#9b59b6"); 32 | 33 | // 7. Parallelogram 34 | shapes[6].polygon("0,400 100,300 300,300 200,400").fill("#34495e"); 35 | }); 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-25 Ionică Bizău (https://ionicabizau.net) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tangram.svg", 3 | "version": "1.0.9", 4 | "description": "The Tangram Square game in the browser.", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/IonicaBizau/tangram.git" 12 | }, 13 | "keywords": [ 14 | "tangram", 15 | "svg", 16 | "html", 17 | "js", 18 | "browser", 19 | "game", 20 | "puzzle" 21 | ], 22 | "author": "Ionică Bizău (https://ionicabizau.net)", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/IonicaBizau/tangram/issues" 26 | }, 27 | "homepage": "https://github.com/IonicaBizau/tangram#readme", 28 | "files": [ 29 | "bin/", 30 | "app/", 31 | "lib/", 32 | "dist/", 33 | "src/", 34 | "scripts/", 35 | "resources/", 36 | "menu/", 37 | "cli.js", 38 | "index.js", 39 | "index.d.ts", 40 | "package-lock.json", 41 | "bloggify.js", 42 | "bloggify.json", 43 | "bloggify/" 44 | ], 45 | "blah": { 46 | "ex_img": "http://i.imgur.com/jQZV10U.png", 47 | "ex_url": "https://ionicabizau.github.io/tangram/", 48 | "title": "Tangram Puzzle", 49 | "description": "Check out [my blog post about this](http://ionicabizau.net/blog/31-the-amazing-tangram-square)." 50 | } 51 | } -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | .graph { 2 | z-index: 9; 3 | position: fixed; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | bottom: 0; 8 | } 9 | 10 | .title { 11 | position: fixed; 12 | z-index: 10; 13 | pointer-events: none; 14 | color: #2D8BC9; 15 | left: 55px; 16 | border: 5px solid; 17 | padding-bottom: 3px; 18 | top: -5px; 19 | padding-right: 20px; 20 | } 21 | 22 | .title h1 { 23 | display: inline; 24 | padding: 0px; 25 | padding-left: 20px; 26 | } 27 | 28 | 29 | polygon { 30 | -webkit-transition: all 0.5s ease; 31 | -moz-transition: all 0.5s ease; 32 | -ms-transition: all 0.5s ease; 33 | -o-transition: all 0.5s ease; 34 | transition: all 0.5s ease; 35 | cursor: pointer; 36 | } 37 | 38 | ul { 39 | list-style: none; 40 | padding: 0; 41 | } 42 | .sidebar { 43 | background: #3498DB; 44 | position: fixed; 45 | z-index: 999; 46 | } 47 | 48 | .sidebar span.octicon { 49 | font-size: 40px; 50 | color: #fff; 51 | padding: 14px; 52 | } 53 | 54 | .sidebar-item { 55 | text-align: center; 56 | border-bottom: 5px solid #2d8bc9; 57 | cursor: pointer; 58 | } 59 | 60 | .sidebar-item:hover { 61 | background: #2d8bc9; 62 | } 63 | 64 | .made-with-heart a, 65 | .made-with-heart a:hover { 66 | color: #2d8bc9; 67 | } 68 | 69 | .made-with-heart a:hover { 70 | text-decoration: underline; 71 | } 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 🌟 Contributing 2 | 3 | Want to contribute to this project? Great! Please read these quick steps to streamline the process and avoid unnecessary tasks. ✨ 4 | 5 | ## 💬 Discuss Changes 6 | Start by opening an issue in the repository using the [bug tracker][1]. Describe your proposed contribution or the bug you've found. If relevant, include platform info and screenshots. 🖼️ 7 | 8 | Wait for feedback before proceeding unless the fix is straightforward, like a typo. 📝 9 | 10 | ## 🔧 Fixing Issues 11 | 12 | Fork the project and create a branch for your fix, naming it `some-great-feature` or `some-issue-fix`. Commit changes while following the [code style][2]. If the project has tests, add one. ✅ 13 | 14 | If a `package.json` or `bower.json` exists, add yourself to the `contributors` array; create it if it doesn't. 🙌 15 | 16 | ```json 17 | { 18 | "contributors": [ 19 | "Your Name (http://your.website)" 20 | ] 21 | } 22 | ``` 23 | 24 | ## 📬 Creating a Pull Request 25 | Open a pull request and reference the initial issue (e.g., *fixes #*). Provide a clear title and consider adding visual aids for clarity. 📊 26 | 27 | ## ⏳ Wait for Feedback 28 | Your contributions will be reviewed. If feedback is given, update your branch as needed, and the pull request will auto-update. 🔄 29 | 30 | ## 🎉 Everyone Is Happy! 31 | Your contributions will be merged, and everyone will appreciate your effort! 😄❤️ 32 | 33 | Thanks! 🤩 34 | 35 | [1]: /issues 36 | [2]: https://github.com/IonicaBizau/code-style -------------------------------------------------------------------------------- /js/crossy.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Crossy=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2 | 3 | 4 | 5 | Tangram 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Tangram

21 |
22 |
23 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /js/css.rotate.js: -------------------------------------------------------------------------------- 1 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.CSSRotate=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o" + 7 | "
  • Left click: rotate left
  • " + 8 | "
  • Right click: rotate right
  • " + 9 | "
  • CTRL + left click: flip
  • " + 10 | "
  • Hold left click + move: drag
  • " + 11 | "" + 12 | "
    " + 13 | " with by Ionică Bizău" + 14 | "
    " 15 | }) 16 | }; 17 | window.addEventListener("load", function () { 18 | 19 | var t = new SVG(document.querySelector(".graph")).size("100%", "100%") 20 | , winSize = { 21 | w: window.innerWidth 22 | , h: window.innerHeight 23 | } 24 | , elements = t.group().id("elements") 25 | , shapes = [ 26 | elements.group() 27 | , elements.group() 28 | , elements.group() 29 | , elements.group() 30 | , elements.group() 31 | , elements.group() 32 | , elements.group() 33 | ] 34 | , size = winSize.w / 3.5 35 | , half = size / 2 36 | , quart = half / 2 37 | , q3 = quart * 3 38 | , leftTopCorner = { 39 | x: winSize.w / 2 - size / 2 40 | , y: 30 41 | } 42 | ; 43 | 44 | // 1. Big Triangle 45 | shapes[0].polygon( 46 | "0,0 " + half + "," + half + " " + size + ",0" 47 | ).fill("#e74c3c"); 48 | 49 | // 2. Big Triangle 50 | shapes[1].polygon("0,0 " + half + "," + half + " 0," + size).fill("#e67e22"); 51 | 52 | // 3. Medium Triangle 53 | shapes[2].polygon(size + "," + size + " " + half + "," + size + " " + size + "," + half).fill("#f1c40f"); 54 | 55 | // 4. Small Triangle 56 | shapes[3].polygon(size + ",0 " + q3 + "," + quart + " " + size + "," + half).fill("#2ecc71"); 57 | 58 | // 5. Small Triangle 59 | shapes[4].polygon(half + "," + half + " " + quart + "," + q3 + " " + q3 + "," + q3).fill("#3498db"); 60 | 61 | // 6. Square 62 | shapes[5].polygon(half + "," + half + " " + q3 + "," + q3 + " " + size + "," + half + " " + q3 + "," + quart).fill("#9b59b6"); 63 | 64 | // 7. Parallelogram 65 | shapes[6].polygon("0," + size + " " + quart + "," + q3 + " " + q3 + "," + q3 + " " + half + "," + size).fill("#34495e"); 66 | 67 | Crossy("polygon", "transformOrigin", "center"); 68 | Crossy("polygon", "transformBox", "fill-box"); 69 | Crossy("polygon", "transition", "all 500 ease"); 70 | 71 | shapes.forEach(function (c) { 72 | var moved = false; 73 | var angle = 0; 74 | var cPol = c.children()[0]; 75 | c.translate(leftTopCorner.x, leftTopCorner.y); 76 | c.draggy(); 77 | c.on("dragmove", function () { 78 | moved = true; 79 | }); 80 | 81 | cPol.on("mousedown", function () { 82 | moved = false; 83 | }); 84 | 85 | cPol.on("contextmenu", function (e) { 86 | e.preventDefault(); 87 | }); 88 | 89 | cPol.on("mouseup", function (e) { 90 | if (!moved) { 91 | var t = this.node.style.transform; 92 | 93 | if (e.ctrlKey) { 94 | this.node._scale = (this.node._scale || 1) === 1 ? -1 : 1; 95 | } else { 96 | angle += (e.button === 2 ? 1 : -1) * 45; 97 | } 98 | 99 | Crossy(this.node, "transform", "rotate(" + angle + "deg) scaleX(" + (this.node._scale || 1) + ")"); 100 | } 101 | moved = false; 102 | e.preventDefault(); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | # Tangram Puzzle 21 | 22 | [![Support me on Patreon][badge_patreon]][patreon] [![Buy me a book][badge_amazon]][amazon] [![PayPal][badge_paypal_donate]][paypal-donations] [![Ask me anything](https://img.shields.io/badge/ask%20me-anything-1abc9c.svg)](https://github.com/IonicaBizau/ama) [![Version](https://img.shields.io/npm/v/tangram.svg.svg)](https://www.npmjs.com/package/tangram.svg) [![Downloads](https://img.shields.io/npm/dt/tangram.svg.svg)](https://www.npmjs.com/package/tangram.svg) [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/@johnnyb?utm_source=github&utm_medium=button&utm_term=johnnyb&utm_campaign=github) 23 | 24 | Buy Me A Coffee 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | > The Tangram Square game in the browser. 33 | 34 | 35 | 36 | 37 | 38 | 39 | Check out [my blog post about this](http://ionicabizau.net/blog/31-the-amazing-tangram-square). 40 | 41 | 42 | 43 | 44 | 45 | 46 | [![tangram.svg](http://i.imgur.com/jQZV10U.png)](https://ionicabizau.github.io/tangram/) 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | ## :question: Get Help 75 | 76 | There are few ways to get help: 77 | 78 | 79 | 80 | 1. Please [post questions on Stack Overflow](https://stackoverflow.com/questions/ask). You can open issues with questions, as long you add a link to your Stack Overflow question. 81 | 2. For bug reports and feature requests, open issues. :bug: 82 | 3. For direct and quick help, you can [use Codementor](https://www.codementor.io/johnnyb). :rocket: 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | ## :yum: How to contribute 102 | Have an idea? Found a bug? See [how to contribute][contributing]. 103 | 104 | 105 | ## :sparkling_heart: Support my projects 106 | I open-source almost everything I can, and I try to reply to everyone needing help using these projects. Obviously, 107 | this takes time. You can integrate and use these projects in your applications *for free*! You can even change the source code and redistribute (even resell it). 108 | 109 | However, if you get some profit from this or just want to encourage me to continue creating stuff, there are few ways you can do it: 110 | 111 | 112 | - Starring and sharing the projects you like :rocket: 113 | - [![Buy me a book][badge_amazon]][amazon]—I love books! I will remember you after years if you buy me one. :grin: :book: 114 | - [![PayPal][badge_paypal]][paypal-donations]—You can make one-time donations via PayPal. I'll probably buy a ~~coffee~~ tea. :tea: 115 | - [![Support me on Patreon][badge_patreon]][patreon]—Set up a recurring monthly donation and you will get interesting news about what I'm doing (things that I don't share with everyone). 116 | - **Bitcoin**—You can send me bitcoins at this address (or scanning the code below): `1P9BRsmazNQcuyTxEqveUsnf5CERdq35V6` 117 | 118 | ![](https://i.imgur.com/z6OQI95.png) 119 | 120 | 121 | Thanks! :heart: 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | ## :scroll: License 147 | 148 | [MIT][license] © [Ionică Bizău][website] 149 | 150 | 151 | 152 | 153 | 154 | 155 | [license]: /LICENSE 156 | [website]: https://ionicabizau.net 157 | [contributing]: /CONTRIBUTING.md 158 | [docs]: /DOCUMENTATION.md 159 | [badge_patreon]: https://ionicabizau.github.io/badges/patreon.svg 160 | [badge_amazon]: https://ionicabizau.github.io/badges/amazon.svg 161 | [badge_paypal]: https://ionicabizau.github.io/badges/paypal.svg 162 | [badge_paypal_donate]: https://ionicabizau.github.io/badges/paypal_donate.svg 163 | [patreon]: https://www.patreon.com/ionicabizau 164 | [amazon]: http://amzn.eu/hRo9sIZ 165 | [paypal-donations]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RVXDDLKKLQRJW 166 | -------------------------------------------------------------------------------- /js/svg.select.min.js: -------------------------------------------------------------------------------- 1 | /*! svg.select.js - v1.0.5 - 2015-06-26 2 | * https://github.com/Fuzzyma/svg.select.js 3 | * Copyright (c) 2015 Ulrich-Matthias Schäfer; Licensed MIT */ 4 | (function(a){function b(a){this.el=a,this.parent=a.parent(SVG.Nested)||a.parent(SVG.Doc),a.remember("_selectHandler",this),this.pointSelection={isSelected:!1},this.rectSelection={isSelected:!1}}b.prototype.init=function(b,c){var d=this.el.bbox();this.options={};for(var e in this.el.select.defaults)this.options[e]=this.el.select.defaults[e],c[e]!==a&&(this.options[e]=c[e]);this.nested=(this.nested||this.parent.nested()).size(d.width||1,d.height||1).transform(this.el.ctm()).move(d.x,d.y),this.options.deepSelect&&-1!==["line","polyline","polygon"].indexOf(this.el.type)?this.selectPoints(b):this.selectRect(b),this.observe(),this.cleanup()},b.prototype.selectPoints=function(a){return this.pointSelection.isSelected=a,this.pointSelection.set?this:(this.pointSelection.set=this.parent.set(),this.drawCircles(),this)},b.prototype.getPointArray=function(){var a=this.el.bbox();return this.el.array().valueOf().map(function(b){return[b[0]-a.x,b[1]-a.y]})},b.prototype.drawCircles=function(){for(var a=this,b=this.getPointArray(),c=0,d=b.length;d>c;++c)this.pointSelection.set.add(this.nested.circle(this.options.radius).center(b[c][0],b[c][1]).addClass(this.options.classPoints).addClass(this.options.classPoints+"_point").mousedown(function(b){return function(c){c=c||window.event,c.preventDefault?c.preventDefault():c.returnValue=!1,a.el.fire("point",{x:c.pageX,y:c.pageY,i:b,event:c})}}(c)))},b.prototype.updatePointSelection=function(){var a=this.getPointArray();this.pointSelection.set.each(function(b){(this.cx()!==a[b][0]||this.cy()!==a[b][1])&&this.center(a[b][0],a[b][1])})},b.prototype.updateRectSelection=function(){var a=this.el.bbox();this.rectSelection.set.get(0).attr({width:a.width,height:a.height}),this.options.points&&(this.rectSelection.set.get(2).center(a.width,0),this.rectSelection.set.get(3).center(a.width,a.height),this.rectSelection.set.get(4).center(0,a.height),this.rectSelection.set.get(5).center(a.width/2,0),this.rectSelection.set.get(6).center(a.width,a.height/2),this.rectSelection.set.get(7).center(a.width/2,a.height),this.rectSelection.set.get(8).center(0,a.height/2)),this.options.rotationPoint&&this.rectSelection.set.get(9).center(a.width/2,20)},b.prototype.selectRect=function(a){function b(a){return function(b){b=b||window.event,b.preventDefault?b.preventDefault():b.returnValue=!1,c.el.fire(a,{x:b.pageX,y:b.pageY,event:b})}}var c=this,d=this.el.bbox();this.rectSelection.isSelected=a,this.rectSelection.set=this.rectSelection.set||this.parent.set(),this.rectSelection.set.get(0)||this.rectSelection.set.add(this.nested.rect(d.width,d.height).addClass(this.options.classRect)),this.options.points&&!this.rectSelection.set.get(1)&&(this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0,0).attr("class",this.options.classPoints+"_lt").mousedown(b("lt"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width,0).attr("class",this.options.classPoints+"_rt").mousedown(b("rt"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width,d.height).attr("class",this.options.classPoints+"_rb").mousedown(b("rb"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0,d.height).attr("class",this.options.classPoints+"_lb").mousedown(b("lb"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width/2,0).attr("class",this.options.classPoints+"_t").mousedown(b("t"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width,d.height/2).attr("class",this.options.classPoints+"_r").mousedown(b("r"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width/2,d.height).attr("class",this.options.classPoints+"_b").mousedown(b("b"))),this.rectSelection.set.add(this.nested.circle(this.options.radius).center(0,d.height/2).attr("class",this.options.classPoints+"_l").mousedown(b("l"))),this.rectSelection.set.each(function(){this.addClass(c.options.classPoints)})),this.options.rotationPoint&&!this.rectSelection.set.get(9)&&this.rectSelection.set.add(this.nested.circle(this.options.radius).center(d.width/2,20).attr("class",this.options.classPoints+"_rot").mousedown(function(a){a=a||window.event,a.preventDefault?a.preventDefault():a.returnValue=!1,c.el.fire("rot",{x:a.pageX,y:a.pageY,event:a})}))},b.prototype.handler=function(){var a=this.el.bbox();this.nested.size(a.width||1,a.height||1).transform(this.el.ctm()).move(a.x,a.y),this.rectSelection.isSelected&&this.updateRectSelection(),this.pointSelection.isSelected&&this.updatePointSelection()},b.prototype.observe=function(){var a=this;if(MutationObserver)if(this.rectSelection.isSelected||this.pointSelection.isSelected)this.observerInst=this.observerInst||new MutationObserver(function(){a.handler()}),this.observerInst.observe(this.el.node,{attributes:!0});else try{this.observerInst.disconnect(),delete this.observerInst}catch(b){}else this.el.off("DOMAttrModified.select"),(this.rectSelection.isSelected||this.pointSelection.isSelected)&&this.el.on("DOMAttrModified.select",function(){a.handler()})},b.prototype.cleanup=function(){!this.rectSelection.isSelected&&this.rectSelection.set&&(this.rectSelection.set.each(function(){this.remove()}),this.rectSelection.set.clear(),delete this.rectSelection.set),!this.pointSelection.isSelected&&this.pointSelection.set&&(this.pointSelection.set.each(function(){this.remove()}),this.pointSelection.set.clear(),delete this.pointSelection.set),this.pointSelection.isSelected||this.rectSelection.isSelected||(this.nested.remove(),delete this.nested)},SVG.extend(SVG.Element,{select:function(c,d){"object"==typeof c&&(d=c,c=!0);var e=this.remember("_selectHandler")||new b(this);return e.init(c===a?!0:c,d||{}),this}}),SVG.Element.prototype.select.defaults={points:!0,classRect:"svg_select_boundingRect",classPoints:"svg_select_points",radius:7,rotationPoint:!0,deepSelect:!1}}).call(this); -------------------------------------------------------------------------------- /js/svg.resize.min.js: -------------------------------------------------------------------------------- 1 | /*! svg.resize.js - v1.1.0 - 2015-06-22 2 | * https://github.com/Fuzzyma/svg.resize.js 3 | * Copyright (c) 2015 Ulrich-Matthias Schäfer; Licensed MIT */ 4 | (function(){function a(a){a.remember("_resizeHandler",this),this.el=a,this.parameters={},this.lastUpdateCall=null,this.p=a.doc().node.createSVGPoint()}a.prototype.transformPoint=function(a,b,c){return this.p.x=a-(this.offset.x-window.pageXOffset),this.p.y=b-(this.offset.y-window.pageYOffset),this.p.matrixTransform(c||this.m)},a.prototype.init=function(a){var b=this;if(this.stop(),"stop"!==a){this.options={};for(var c in this.el.resize.defaults)this.options[c]=this.el.resize.defaults[c],"undefined"!=typeof a[c]&&(this.options[c]=a[c]);this.el.on("lt.resize",function(a){b.resize(a||window.event)}),this.el.on("rt.resize",function(a){b.resize(a||window.event)}),this.el.on("rb.resize",function(a){b.resize(a||window.event)}),this.el.on("lb.resize",function(a){b.resize(a||window.event)}),this.el.on("t.resize",function(a){b.resize(a||window.event)}),this.el.on("r.resize",function(a){b.resize(a||window.event)}),this.el.on("b.resize",function(a){b.resize(a||window.event)}),this.el.on("l.resize",function(a){b.resize(a||window.event)}),this.el.on("rot.resize",function(a){b.resize(a||window.event)}),this.el.on("point.resize",function(a){b.resize(a||window.event)}),this.update()}},a.prototype.stop=function(){return this.el.off("lt.resize"),this.el.off("rt.resize"),this.el.off("rb.resize"),this.el.off("lb.resize"),this.el.off("t.resize"),this.el.off("r.resize"),this.el.off("b.resize"),this.el.off("l.resize"),this.el.off("rot.resize"),this.el.off("point.resize"),this},a.prototype.resize=function(a){var b=this;if(this.m=this.el.node.getScreenCTM().inverse(),this.offset={x:window.pageXOffset,y:window.pageYOffset},this.parameters={p:this.transformPoint(a.detail.event.clientX,a.detail.event.clientY),x:a.detail.x,y:a.detail.y,box:this.el.bbox(),rotation:this.el.transform().rotation},void 0!==a.detail.i){var c=this.el.array().valueOf();this.parameters.i=a.detail.i,this.parameters.pointCoords=[c[a.detail.i][0],c[a.detail.i][1]]}switch(a.type){case"lt":this.calc=function(a,b){var c=this.snapToGrid(a,b);this.parameters.box.width-c[0]>0&&this.parameters.box.height-c[1]>0&&this.el.move(this.parameters.box.x+c[0],this.parameters.box.y+c[1]).size(this.parameters.box.width-c[0],this.parameters.box.height-c[1])};break;case"rt":this.calc=function(a,b){var c=this.snapToGrid(a,b,2);this.parameters.box.width+c[0]>0&&this.parameters.box.height-c[1]>0&&this.el.move(this.parameters.box.x,this.parameters.box.y+c[1]).size(this.parameters.box.width+c[0],this.parameters.box.height-c[1])};break;case"rb":this.calc=function(a,b){var c=this.snapToGrid(a,b,0);this.parameters.box.width+c[0]>0&&this.parameters.box.height+c[1]>0&&this.el.move(this.parameters.box.x,this.parameters.box.y).size(this.parameters.box.width+c[0],this.parameters.box.height+c[1])};break;case"lb":this.calc=function(a,b){var c=this.snapToGrid(a,b,1);this.parameters.box.width-c[0]>0&&this.parameters.box.height+c[1]>0&&this.el.move(this.parameters.box.x+c[0],this.parameters.box.y).size(this.parameters.box.width-c[0],this.parameters.box.height+c[1])};break;case"t":this.calc=function(a,b){var c=this.snapToGrid(a,b,2);this.parameters.box.height-c[1]>0&&this.el.move(this.parameters.box.x,this.parameters.box.y+c[1]).height(this.parameters.box.height-c[1])};break;case"r":this.calc=function(a,b){var c=this.snapToGrid(a,b,0);this.parameters.box.width+c[0]>0&&this.el.move(this.parameters.box.x,this.parameters.box.y).width(this.parameters.box.width+c[0])};break;case"b":this.calc=function(a,b){var c=this.snapToGrid(a,b,0);this.parameters.box.height+c[1]>0&&this.el.move(this.parameters.box.x,this.parameters.box.y).height(this.parameters.box.height+c[1])};break;case"l":this.calc=function(a,b){var c=this.snapToGrid(a,b,1);this.parameters.box.width-c[0]>0&&this.el.move(this.parameters.box.x+c[0],this.parameters.box.y).width(this.parameters.box.width-c[0])};break;case"rot":this.calc=function(a,b){var c={x:a+this.parameters.p.x,y:b+this.parameters.p.y},d=Math.atan2(this.parameters.p.y-this.parameters.box.y-this.parameters.box.height/2,this.parameters.p.x-this.parameters.box.x-this.parameters.box.width/2),e=Math.atan2(c.y-this.parameters.box.y-this.parameters.box.height/2,c.x-this.parameters.box.x-this.parameters.box.width/2),f=180*(e-d)/Math.PI;this.el.center(this.parameters.box.cx,this.parameters.box.cy).rotate(this.parameters.rotation+f-f%this.options.snapToAngle,this.parameters.box.cx,this.parameters.box.cy)};break;case"point":this.calc=function(a,b){var c=this.snapToGrid(a,b,this.parameters.pointCoords[0],this.parameters.pointCoords[1]),d=this.el.array().valueOf();d[this.parameters.i][0]=this.parameters.pointCoords[0]+c[0],d[this.parameters.i][1]=this.parameters.pointCoords[1]+c[1],this.el.plot(d)}}SVG.on(window,"mousemove.resize",function(a){b.update(a||window.event)}),SVG.on(window,"mouseup.resize",function(){b.done()})},a.prototype.update=function(a){if(!a)return void(this.lastUpdateCall&&this.calc(this.lastUpdateCall[0],this.lastUpdateCall[1]));var b=this.transformPoint(a.clientX,a.clientY),c=b.x-this.parameters.p.x,d=b.y-this.parameters.p.y;this.lastUpdateCall=[c,d],this.calc(c,d)},a.prototype.done=function(){this.lastUpdateCall=null,SVG.off(window,"mousemove.resize"),SVG.off(window,"mouseup.resize"),this.el.fire("resizedone")},a.prototype.snapToGrid=function(a,b,c,d){var e;return d?e=[(c+a)%this.options.snapToGrid,(d+b)%this.options.snapToGrid]:(c=null==c?3:c,e=[(this.parameters.box.x+a+(1&c?0:this.parameters.box.width))%this.options.snapToGrid,(this.parameters.box.y+b+(2&c?0:this.parameters.box.height))%this.options.snapToGrid]),a-=(Math.abs(e[0]) constraint.maxX - width) { 170 | x = constraint.maxX - width; 171 | } 172 | 173 | if (constraint.minY !== null && y < constraint.minY) { 174 | y = constraint.minY; 175 | } else if (constraint.maxY !== null && y > constraint.maxY - height) { 176 | y = constraint.maxY - height; 177 | } 178 | 179 | element.move(x, y); 180 | } 181 | 182 | // Calculate the total movement 183 | delta.movedX = x - element.startPosition.x; 184 | delta.movedY = y - element.startPosition.y; 185 | 186 | // Invoke any callbacks 187 | element.node.dispatchEvent(new CustomEvent("dragmove", { 188 | detail: { 189 | delta: delta 190 | , event: event 191 | } 192 | })); 193 | } 194 | }; 195 | 196 | // When dragging ends 197 | end = function(event) { 198 | event = event || window.event; 199 | 200 | // Calculate move position 201 | var delta = { 202 | x: event.pageX - element.startEvent.pageX 203 | , y: event.pageY - element.startEvent.pageY 204 | , zoom: element.startPosition.zoom 205 | }; 206 | 207 | // Reset store 208 | element.startEvent = null; 209 | element.startPosition = null; 210 | 211 | // Remove while and end events to window 212 | SVG.off(window, "mousemove", drag); 213 | SVG.off(window, "touchmove", drag); 214 | SVG.off(window, "mouseup", end); 215 | SVG.off(window, "touchend", end); 216 | 217 | // Invoke any callbacks 218 | element.node.dispatchEvent(new CustomEvent("dragend", { 219 | detail: { 220 | delta: { 221 | x: 0 222 | , y: 0 223 | } 224 | , event: event 225 | } 226 | })); 227 | }; 228 | 229 | // Bind mousedown event 230 | element.on("mousedown", start); 231 | element.on("touchstart", start); 232 | 233 | // Disable draggable 234 | element.fixed = function() { 235 | element.off("mousedown", start); 236 | element.off("touchstart", start); 237 | 238 | SVG.off(window, "mousemove", drag); 239 | SVG.off(window, "touchmove", drag); 240 | SVG.off(window, "mouseup", end); 241 | SVG.off(window, "touchend", end); 242 | 243 | start = drag = end = null; 244 | 245 | return element; 246 | }; 247 | 248 | return this; 249 | } 250 | }); 251 | }).call(this); 252 | --------------------------------------------------------------------------------