Search code examples
csssafaritransform

Safari zoom causes problems in my donut circle display, what can I do to fix this?


I have a need to achieve a display with a circle with a text score in it (number 1 to 10) Then around that circle, I would like animate a display that incates a gauge getting fuller starting at 0 moving to the relevant number.

This all works well in all browser except Safari. The moment you zoom in / out, the display breaks and the circles no longer overlap.

We are using CSS transform. Is there any alternative recommendations to achieve something similiar?Sample screen showing the rendering

Here is a sample piece of html to demonstrate the problem in Safari

.doughnut {
  transform: translateX(2px);
  margin: auto;
}

.score-text {
  fill: #ffffff;
}

.score-text.secondary {
  fill: #1B3A57;
}

.gradient--start {
  stop-color: hsl(209, 52.6%, 85%);
}

.gradient--end {
  stop-color: #1B3A57;
}

svg {
  cursor: pointer;
}

.circle-chart__background {
  stroke: #ffffff;
  stroke-width: 6;
  fill: #1B3A57;
  transition: fill 0.4s ease;
}

.circle-chart__background.secondary {
  fill: #eaeaea;
  transition: fill 0.4s ease;
  margin-left: 4px;
}

.circle-chart__circle {
  animation: circle-chart-fill 2s reverse;
  transform: rotate(-90deg);
  transform-origin: center;
  stroke: url(#grad2);
  stroke-width: 2;
  stroke-linecap: round;
  fill: none;
}

.circle-chart__circle--negative {
  transform: rotate(-90deg) scale(1, -1);
}

@keyframes circle-chart-fill {
  to {
    stroke-dasharray: 0 100;
  }
}

@keyframes circle-chart-appear {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

html {
  font-family: sans-serif;
  padding-right: 1em;
  padding-left: 1em;
}
<div class="doughnut">
  <div>
    <svg width="100%" height="100%" viewBox="0 0 42 42" class="doughnut">
          <defs>
            <linearGradient id="grad2" x1="90%" y1="95%" x2="0%" y2="30%">
              <stop offset="0%" class="gradient--start" stop-opacity="1" />
              <stop offset="100%" class="gradient--end" />
            </linearGradient>
          </defs>

          <circle
            class="circle-chart__background secondary"
            cx="19"
            r="15.91549431"
            cy="23"
          />
          <circle
            class="circle-chart__circle"
            cx="19"
            r="15.91549431"
            cy="19"
            stroke-dasharray="60 40"
            v-if="score"
          />
          <g class="circle-chart__info">
            <text
              class="score-text secondary"
              x="19"
              y="23"
              alignment-baseline="central"
              text-anchor="middle"
              font-size="8"
            >
              6
            </text>
          </g>
        </svg>
  </div>
</div>

Here is a video that demonstrates the issue https://vimeo.com/848865835?share=copy


Solution

  • You're compensating CSS transform by making your circles excentric.

    You might keep circles concentric at (19, 23) and use transform attribute on circle instead:

    .doughnut {
      transform: translateX(2px);
      margin: auto;
    }
    
    .score-text {
      fill: #ffffff;
    }
    
    .score-text.secondary {
      fill: #1B3A57;
    }
    
    .gradient--start {
      stop-color: hsl(209, 52.6%, 85%);
    }
    
    .gradient--end {
      stop-color: #1B3A57;
    }
    
    svg {
      cursor: pointer;
    }
    
    .circle-chart__background {
      stroke: #ffffff;
      stroke-width: 6;
      fill: #1B3A57;
      transition: fill 0.4s ease;
    }
    
    .circle-chart__background.secondary {
      fill: #eaeaea;
      transition: fill 0.4s ease;
      margin-left: 4px;
    }
    
    .circle-chart__circle {
      animation: circle-chart-fill 2s reverse;
      stroke: url(#grad2);
      stroke-width: 2;
      stroke-linecap: round;
      fill: none;
    }
    
    .circle-chart__circle--negative {
      transform: rotate(-90deg) scale(1, -1);
    }
    
    @keyframes circle-chart-fill {
      to {
        stroke-dasharray: 0 100;
      }
    }
    
    @keyframes circle-chart-appear {
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }
    
    html {
      font-family: sans-serif;
      padding-right: 1em;
      padding-left: 1em;
    }
    <div class="doughnut">
      <div>
        <svg width="100%" height="100%" viewBox="0 0 42 42" class="doughnut">
              <defs>
                <linearGradient id="grad2" x1="90%" y1="95%" x2="0%" y2="30%">
                  <stop offset="0%" class="gradient--start" stop-opacity="1" />
                  <stop offset="100%" class="gradient--end" />
                </linearGradient>
              </defs>
    
              <circle
                class="circle-chart__background secondary"
                cx="19"
                r="15.91549431"
                cy="23"
              />
              <circle
                class="circle-chart__circle"
                cx="19"
                r="15.91549431"
                cy="23"
                stroke-dasharray="60 40"
                v-if="score"
                transform="rotate(-90, 19, 23)"
              />
              <g class="circle-chart__info">
                <text
                  class="score-text secondary"
                  x="19"
                  y="23"
                  alignment-baseline="central"
                  text-anchor="middle"
                  font-size="8"
                >
                  6
                </text>
              </g>
            </svg>
      </div>
    </div>