Dynamically divide donut to the slices

39 Views Asked by At

I work on the Nuxt.js (v.2.15.0) app. I have a component like a donut with 3 sectors divided to 4 another sub-sectors (here). I change the colors of the sectors dynamically depending on values I recieve via websockets. My code for component:

<template>
    <div class="lap is-flex is-flex-direction-column">
        <div class="live-label is-flex is-justify-content-center" v-text="title" />
        <div class="lap-content">
            <div class="circle">
                <ul class="circle__wrapper">
                    <li v-for="index in count" :key="index" class="circle__section" />
                </ul>
                <div
                    class="
                        circle__center
                        is-flex is-align-items-center is-justify-content-center
                    "
                >
                    <div class="live-label--large" v-text="speedValue" />
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import { msToLapTime } from '@/utils'

export default {
    props: {
        title: {
            type: String,
            default: 'Lap',
        },
        value: {
            type: Number,
            default: 0,
        },
        currentSector: {
            type: Number,
            default: 0,
        },
        sectors: {
            type: Object,
            default: () => {},
        },
        count: {
            type: Number,
            default: 12,
        },
    },
    computed: {
        speedValue() {
            return this.value > 0
                ? msToLapTime(this.value, this.$dateFns).slice(0, 7)
                : '00:00.0'
        },
    },
    watch: {
        currentSector() {
            this.setSegmentColour()
        },
    },
    mounted() {
        this.setSegmentColour()
    },
    methods: {
        setSegmentColour() {
            const sectionArr = document.querySelectorAll('.circle__section')
            if (this.currentSector === 1) {
                for (let i = 0; i < this.count; i++) {
                    if (i < 4) {
                        sectionArr[i].classList = 'circle__section filled'
                    } else {
                        sectionArr[i].className = 'circle__section'
                    }
                }
            } else if (this.currentSector === 2) {
                for (let i = 0; i < 12; i++) {
                    if (i < 4) {
                        sectionArr[i].classList.add(this.sectors.s1Colour)
                    } else if (i >= 4 && i < 8) {
                        sectionArr[i].classList = 'circle__section filled'
                    } else {
                        sectionArr[i].className = 'circle__section'
                    }
                }
            } else if (this.currentSector === 3) {
                for (let i = 0; i < 12; i++) {
                    if (i < 4) {
                        sectionArr[i].classList.add(this.sectors.s1Colour)
                    } else if (i >= 4 && i < 8) {
                        sectionArr[i].classList.add(this.sectors.s2Colour)
                    } else {
                        sectionArr[i].classList = 'circle__section filled'
                    }
                }
            }
        },
    },
}
</script>

And the style for this donut is here:

.circle {
    position: relative;

    &__wrapper {
        border-radius: 50%;
        position: relative;
        padding: 0;
        margin: 1em auto;
        list-style: none;
        overflow: hidden;

        &::before {
            content: '';
            position: absolute;
            z-index: 2;
            width: calc(100% + 2px);
            height: calc(100% + 2px);
            top: -1px;
            left: -1px;
            border-radius: 50%;
            mask-image: radial-gradient(farthest-side, transparent calc(100% - 2px), #fff 100%);
            transform: rotate(1deg);
        }

        @include grid-elem-size('height', 2.5);
        @include grid-elem-size('width', 2.5);
    }

    &__section {
        position: absolute;
        top: 0;
        right: 0;
        width: 50%;
        height: 50%;
        background-color: #656565;
        transform-origin: 0% 100%;
        @include circle-fraction(12);

        &.filled {
            background-color: #d0cfcf;
            animation-name: color;
            animation-duration: 2s;
            animation-iteration-count: infinite;
        }
    }

    &__center {
        position: absolute;
        top: calc(50% - #{$grid-size} * 1.1);
        left: calc(50% - #{$grid-size} * 1.1);
        border-radius: 50%;
        background-color: var(--background-color);
        @include grid-elem-size('height', 2.2);
        @include grid-elem-size('width', 2.2);

        &::before {
            content: '';
            position: absolute;
            width: 100%;
            height: 100%;
            border-radius: 50%;
            mask-image: radial-gradient(farthest-side, transparent calc(100% - 1px), #fff 100%);
            transform: rotate(1.5deg);
        }
    }
}

To divide the chart to 12 elements I use a mixin (and include below the mixin I use in the code above in case if someone wants to reproduce it):

$grid-size: #{'(100vh - 185px) / 8'}

@mixin grid-elem-size($property, $size){
    #{$property}: calc(#{$grid-size} * #{$size})!important;
}

@mixin circle-fraction($size) {
    @for $i from 1 through $size {
        &:nth-child(#{$i}) {
            transform: rotate(calc((#{$i} - 1) * (calc(360deg / #{$size})))) skewY(calc(-360deg / #{$size / 2})) translate(4px,-4px);
        }
    }
}

It works well enough for me. And now requirements have been changed.

I need to divide the element dynamically, because now we work with more tracks and some of them possibly has more than 3 sectors, so I should receive the variable (which can be 3~10) and divide the donut to this amount of sectors and every sector divide to 4 sub-sectors. Also every sub-sector needs to keep a space around, as it is now (maybe reduce the space).I'd appreciate any help, even some library would be very helpful. Thanks in advance!

0

There are 0 best solutions below