<div id="app"> 
    <svg version="1.1" id="world" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="1000px" height="600px" viewBox="0 0 1000 600" enable-background="new 0 0 1000 600" xml:space="preserve"> 
      
           <transition name="fade">
             <g v-if="activeIndex > 0" v-on:click="prevPalette">
             <text class="prevBtn" x="130" y="498" style="font-size: 20px; cursor: pointer;" :fill="prevColor" >Prev</text> 
              <rect class="prevBtnRect" x="100" y="465" width="100" rx="5" height="50" :stroke="prevColor" fill="none"/>
             </g>
           </transition>  
          
           <transition name="fade">
             <g v-if="activeIndex < paletteSize-1" v-on:click="nextPalette">
               <text class="nextBtn" x="436" y="498" style="font-size: 20px; cursor: pointer;" :fill="nextColor">Next</text>  
               <rect class="nextBtnRect" x="410" y="465" width="100" height="50" rx="5" :stroke="nextColor" fill="none"/>
             </g>
           </transition>                                           
     

           <g v-for="(value, key, index) in items" class="palettes" > 
             
               <rect x="300" y="270" height="2" width="170" style="stroke:none; fill:none; "/>
             
         <rect x="360" y="270" height="15" width="100" rx="4" :fill=value.shade_600 :class="activepalette == key ? 'active' : 'inactive'" v-on:click="activeIndex=index" />

      </g> 
      
      
      <g class="paletteTitle">        
        <path fill="none" stroke="none" stroke-miterlimit="10" d="M889.5 222.6c0 104.6-84.8 190.4-189.3 190.4s-189.4-85.8-189.4-190.4c0-104.5 84.8-189.3 189.4-189.3S889.5 118 889.5 222.6z" id="Layer_2"/>        
        <use xlink:href="#Layer_2"/>
        <text style="font-size: 25px; text-transform: uppercase;" :fill=activeColor>
          <textPath xlink:href="#Layer_2">
            {{ activepalette }} Palette
          </textPath>
        </text>
      </g>      
      
      <g class="palette" v-for="code in activeValues">       
        <text x="530" y="267" v-fillcolor="code" style="text-transform: uppercase;">  {{ code }} </text> 
        <rect  x="530" y="270" width="160" height="15" rx="3" fill="none" />                       
        <rect  x="530" y="270" width="100" height="15" rx="3" v-fillcolor="code" />  
      </g>   
      
      
      <text class="title" x="20" y="50" style="font-size: 20px;" :fill="activeColor"> Material UI Circular Colour Palette Made With 
        <tspan style="fill:#41B883"> Vue JS </tspan> and 
        <tspan style="fill:#88CE02"> GSAP </tspan> </text> 
  </svg>   
</div>
body {
 background-color: #212121;
 text-align: center;
font-family: 'Roboto Condensed', sans-serif;
}
svg {
  height: 100vh;
  width: 100vw;
}
#world {
 width: 100%;
 height: 100%;
}

.fade-enter-active, .fade-leave-active {
  transition: opacity 1s
}
.fade-enter, .fade-leave-to {
  opacity: 0
}

text { 
  font-size: 18px; text-transform: uppercase; 
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
   user-select: none; 
}
.active { opacity: 1; transition: opacity 2s; }
.inactive { opacity: 0.5; cursor: pointer; }
.palettes { opacity: 0; }
.palette { opacity: 0; }
.paletteTitle { opacity: 0; }
.nav { cursor: pointer; }
Vue.directive('fillcolor', function(el, binding) {  
    el.style.fill =  binding.value;
   el.style.transition = "opacity 1s";
})

new Vue({
  
  el: '#app',
  
  data() {
    return {
      items: {},
      
      activepalette: "red",
      activeColor: "#ef5350", // for red palette
      activeIndex: 0,    
      
      clickedPalette: "red",                
      clickedColor: "#E53935",
      
      nextColor: "#D81B60", 
      prevColor: "#ef5350", 
    };
  },

  created() {
    this.fetchData();     
  }, 
  
  
  watch: {
    
    items(newValue) { 
      this.$nextTick(function() {
         this.createColorWheel();
         this.createSpectrum()
       })  

    },
    
    activeIndex(newValue) {    
      this.assignColors(newValue); 
      this.rotateWheel(newValue);            
    }, 
    
    activepalette(newValue) {
      this.createSpectrum()
    }, 
    
  }, // end of watch
  
  computed: {
    
    activeValues() {
      return _.find(this.items, (item, index) => {
        return index == this.activepalette        
      })
    }, 
    
    paletteSize() {
      return _.size(this.items)  
    }, 
    
  }, // end of computed

  methods: {
    
    fetchData() {
      var self = this;
      var dataURL="https://raw.githubusercontent.com/Krutie/dataRepo/master/palette.json";
      
      $.getJSON(dataURL, function(data) {        
        self.items = data;  
      });
      
    }, // end of fetchData()
    
    createColorWheel() {

      let degrees = $(".palettes")     
      
          let angle = 360/16;
      
          degrees.each(function(index, item) {
            TweenMax.to(item, 2, {
             rotation: angle*index, opacity: 1, transformOrigin: "0% 100%"
            }, 1); 
          });
      
    }, // end of createColorWheel
    
     createSpectrum() {       
        let palette = $(".palette")   
        let paletteTitle = $(".paletteTitle");
        let angle = 360/_.size(palette);
     
        _.map(palette, (item, key) => {            
              TweenMax.to(item, 1, { 
                rotation: angle*key+50, opacity: 1, transformOrigin: "100% 65%", delay: 0.2,
                onComplete: this.createTitleOnAPath
              }, 0.1);        
         }); // end of map              
    }, // end of createSpectrum()
    
    createTitleOnAPath(){
       TweenMax.staggerFromTo($(".paletteTitle"), 0.5,      
           { rotation: 0, opacity: 0 },
           { rotation: -45, opacity: 1, transformOrigin: "60% 55%"
         }, 0.1); // end of TweenMax      
    },
    
    // For Interactive Color Wheel 
    assignColors(newValue){
      
      let k = _.keys(this.items);    
      this.clickedPalette = k[newValue];    
      
      let c = k[newValue];      
      this.clickedColor = this.items[c].shade_600;
              
      let n = k[newValue+1]       
      let p = k[newValue-1]                 
           
      this.fillColor(".title", this.clickedColor);
      
      if (n != undefined  ) {
        this.fillColor(".nextBtn", this.items[n].shade_600 );
        this.strokeColor(".nextBtnRect", this.items[n].shade_600 );
      }                  
      
      if (p != undefined  ) {
         this.fillColor(".prevBtn", this.items[p].shade_600);
         this.strokeColor(".prevBtnRect", this.items[p].shade_600);
      }                    
      
    }, // end of assignColors 
    fillColor(el, shade) {
       TweenMax.staggerTo(el, 1, {
					  fill:shade
				}, 1, 0) 
    },
    strokeColor(el, shade) {
       TweenMax.staggerTo(el, 1, {
					  stroke:shade
				}, 1, 0) 
    },
    rotateWheel(clickedKey) {       
        let degrees = $(".palettes");  let angle = 360/16;      
       _.map(degrees, (index, key) => {
            if(clickedKey == key) {
                 TweenLite.to(index, 1, {
                    rotation: 0+'_short', transformOrigin: "0% 100%",delay: 1.5, onStart: this.erasePrevious,
                }, 0.1);   
            } else if (key == 0) {              
                TweenLite.to(index, 1, {
                    rotation: (clickedKey*angle)+'_short', transformOrigin: "0% 100%", delay: 1
                }, 0.1); 
            } else {
              TweenLite.to(index, 1, {
                rotation: key*angle+'_short', transformOrigin: "0% 100%", delay: 0.5     
                }, 0.1);
            }
        });         
    }, // end of rotateWheel
    
    erasePrevious() { 
       let palette = $(".palette")         
       let angle = 360/_.size(palette);
       let eraseTL = new TimelineMax();
       _.map(palette, (index, key) => {            
            eraseTL.to(index, 1.5, {
                rotation: 0, opacity: 0, transformOrigin: "100% 65%", ease: Circ.easeOut
           }, 0.1); 
       });     
      
       eraseTL.to($(".paletteTitle"), 1.5, {
          rotation: 0, opacity: 0, transformOrigin: "60% 55%", ease: Circ.easeOut, onComplete: this.passNewValues
       }, 0.1);            
      
    }, // end of erasePrevious
    passNewValues() {
      this.activepalette = this.clickedPalette; 
      this.activeColor = this.clickedColor;
    },
    
    prevPalette() { this.activeIndex -= 1; },
    nextPalette() { this.activeIndex += 1;  },
    
    
  }, // end of methods
 
})
View Compiled

External CSS

  1. https://fonts.googleapis.com/css?family=Roboto|Roboto+Condensed

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/TweenMax.min.js
  5. https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js