Rotating sliders causes them to move opposite mouse drag. How to reverse this?

Once my sliders rotate past a certain point, the animation is reversed from the direction of the mouse. I’m hoping there is a way to solve this, but most of my searches have ended in dead-ends.

I’ve made a polar bar chart, with the sliders aligned with the petals of the polar chart, but hidden, and a column chart to represent a series of dependent variables. My goal is to make it look like the user is dragging the bars on the polar chart, and then to show how those changes affect the outcomes in the column chart on the left (as a product of the change and a coefficient).

I’m super new to python in general and have been using dash for about a week now, so I’m sorry if this is an eyesore to most of you:

#%% Setup and variables

vals2020 = np.random.uniform(3.5, 4.5, 24).round(2)
vals = np.random.uniform(3.7, 5, 24).round(2)
bars = np.random.uniform(3.7, 5, 4).round(2)
mean_coeffs = np.random.uniform(.1, .8, 24)
stress_coeffs = np.random.uniform(-.8, .1, 24)
salary_coeffs = np.random.uniform(.1, .8, 24)
engage_coeffs = np.random.uniform(.1, .8, 24)
barlabs= ['lab1', 'lab2', 'lab3', 'lab4']
D = ['var{}'.format(i+1) for i in range(24)]
color = ['#EFBBCC','#22A1A1','#D9D9D9','#3A3385']
color = [j for i in color  for j in 6*[i]]
num_slices = len(vals)
theta = [i  * 360 / num_slices for i in range(num_slices)]
width = [360 / num_slices for _ in range(num_slices)]

# align background and clean up figure
layout_options = {"title": "Fig Title",
                  'title_font_color' : '#0C0050',
                  "title_font_size": 24,
                  "title_x":0.5,
                  'template' : 'simple_white',
                  "polar_radialaxis_visible": False,
                  "polar_radialaxis_showticklabels": False,
                  "polar_angularaxis_showline": False,
                  "polar_angularaxis_showgrid": False,
                  "polar_angularaxis_rotation": 7.5,
                  "polar_angularaxis_showticklabels": True,
                  "polar_angularaxis_visible": True,
                  'polar_angularaxis_tickvals':theta,
                  'polar_angularaxis_tickmode':'array',
                  'polar_angularaxis_ticktext': D,
                  'polar_radialaxis_range': (0,7)}
# clamp values to range 0-5
clamp = lambda n, minn, maxn: max(min(maxn, n), minn)

And here is the dash code:

#%% app layout
app = dash.Dash(__name__)
app.layout = html.Div([
        html.Div([
            html.Div([
                dcc.Graph(id="bar",
                          style={'width':'29vw','height':'45vw'}),               
                dcc.Dropdown(
                            id='dropdown',
                            options=[
                                {'label': '2020', 'value': 'graph1'},
                                {'label': '2021', 'value': 'graph2'},
                            ],
                            value='graph2'),
            ], style={'width':'29vw', 'height': '45vw'}, className='six columns'),
        
            html.Div([
                dcc.Graph(id="graph",
                          style={'width':'60vw','height':'50vw'}), 
                html.Div(id='slider-container'),
                dcc.Input(style={'display': 'none'})                

                ], style={'zIndex':1}, className='six columns'),
    ], className = 'row')
])

#%% Callbacks
@app.callback(Output('slider-container', 'children'),
               [Input('dropdown', 'value')])
def add_sliders(v):
    if v == 'graph1': # chooses input data for fig based on dropdown
        vvals = vals2020.copy()
    else:
        vvals = vals.copy()
    return html.Div(
        [html.Div([
            dcc.Slider(id='slider-{}'.format(i),
                  min = 1,
                  max = 5,
                  value = vvals[i],
                  updatemode='drag',
                  step = .1)],
            style={'position': 'absolute',
                   'zIndex': 2000, # doing this so slider handles on top
                        'width': '20%',
                        'opacity': 0, # hides sliders
                        'left': '63.5vw',
                        'top': '26vw',
                        'transform': 'rotate(' +str(-theta[i]-7.5) + 'deg)',
                        'transform-origin' : 'top left'}
        ) for i in range(len(vals))]
        )
@app.callback(Output('graph', 'figure'),
              [Input('slider-{}'.format(i), 'value') for i in range(len(vals))
               ])
def update_figure(*v):
    # polar moves with sliders
    fig = go.Figure()
    fig.add_trace(go.Barpolar(r=v, theta=theta, width=width,
                              base = 2,
                              marker_color=color, 
                              hoverinfo = 'skip',
                              showlegend = False))

    fig.add_trace(go.Scatterpolar(r=np.array(v)*1.25, theta=theta,
                                      text = v,                                        
                                      showlegend = False, 
                                      hoverinfo = 'none',
                                      textfont = dict(
                                          color = 'white', 
                                          size = 12),
                                      mode= 'text'))
    fig.add_trace(go.Scatterpolar(r = [0], theta= [0],
                                      text = "Quotient</br></br>" + str(
                                          np.array(v).mean().round(2)),
                                      showlegend = False, 
                                      hoverinfo = 'none',
                                      textfont = dict(
                                          color = '#0C0050', 
                                          size = 18),
                                      mode= 'markers + text',
                                      marker_color = 'white',
                                      marker_size = [0,0],
                                      marker_opacity = 1))
    fig.update_layout(layout_options)

    return fig 
@app.callback(Output('bar', 'figure'),
              [Input('slider-{}'.format(i), 'value') for i in range(len(vals))],
              )
def update_bar(*v):
    bbars = bars.copy()
    for i in range(24):
        bbars[0] = np.array(clamp(bbars[0] + mean_coeffs[i]*(v[i]-vals[i]), 0, 5)).round(2)
        bbars[1] = np.array(clamp(bbars[1] + stress_coeffs[i]*(v[i]-vals[i]), 0, 5)).round(2)
        bbars[2] = np.array(clamp(bbars[2] + salary_coeffs[i]*(v[i]-vals[i]), 0, 5)).round(2)
        bbars[3] = np.array(clamp(bbars[3] + engage_coeffs[i]*(v[i]-vals[i]), 0, 5)).round(2)
    bar = go.Figure({'data':[{'x':barlabs,
                        'y':[bbars[0],bbars[1], bbars[2], bbars[3]],
                        'text':bbars,
                        'textposition':'auto',
                        'hoverinfo':'skip',
                        'type':'bar',
                        'marker_color': '#0C0050'}]})
    bar.update_yaxes(range = [0,5])
    return bar
if __name__ == '__main__':
    app.run_server(debug=False)

I also have this CSS added in an assets folder to help with the positioning/styling of the sliders

/* Sliders
–––––––––––––––––––––––––––––––––––––––––––––––––– */

#sliders .element.style {
    z-index: 2000;
}
.rc-slider {
    position: absolute;
    height: 0px;
    padding: 0px 0;
    width: 100%;
    border-radius: 6px;
    box-sizing: border-box;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
#slider-0 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}
#slider-1 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}

#slider-2 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}
#slider-3 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}
#slider-4 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}

#slider-5 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #EFBBCC;
}
#slider-6 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-7 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-8 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-9 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-10 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-11 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #22A1A1;
}
#slider-12 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-13 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-14 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-15 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-16 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-17 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #D9D9D9;
}
#slider-18 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}
#slider-19 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}
#slider-20 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}
#slider-21 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}
#slider-22 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}
#slider-23 .rc-slider-track {
    position: absolute;
    left: 0;
    height: 4px;
    border-radius: 6px;
    background-color: #3A3385;
}

Again, what I want is for the sliders to follow the mouse, basically reverse the range when the sliders are flipped across the axis. I’ve tried changing the orientation to rtl for some of the sliders, but nothing changed. I’ve also tried to use some js to reverse the ranges, but I very quickly got in over my head.

General advice is very much appreciated. Also, this community has been so helpful. I’ve been absolutely amazed at how much useful advice is out there, and you’ll probably notice many snippets from the docs and this forum in my code. I went from zero programming experience to making a dashboard in about a month. You’re all wonderful.