简体   繁体   English

用曲线将输入范围圆弧拇指连接到 slider

[英]Connecting input range arc thumb to slider with curve

I have a solved question Animating range ticks when moving range thumb .我有一个已解决的问题Animating range ticks when moving range thumb From that question, I have a input type="range" with custom appearance - thumb is made like an arc (half circle), span that takes range's value and is designed as a circled thumb and div with mask that act like ticks - steps.从那个问题开始,我有一个自定义外观的input type="range" - 拇指被做成一个弧(半圆), span取范围的值并设计为一个带圆圈的拇指和div ,带有像刻度一样的mask - 步骤.


From this expected result从这个预期的结果

预期结果

I was trying to connect that arc to the slider with a curve.我试图用一条曲线将该弧线连接到 slider。 I tried to use pseudo elements but gradient doesn't sync with gradient on the slider and I cannot make a curve like shown in the picture.我尝试使用伪元素,但渐变与 slider 上的渐变不同步,我无法制作如图所示的曲线。 I also tried using JS canvas to draw that curve and place it on desired place but gradient again doesn't sync - becomes a stationary color.我还尝试使用JS canvas绘制该曲线并将其放置在所需的位置,但渐变再次不同步 - 成为固定颜色。

I thought using a CSS mask but I'm not sure if it's possible to make wanted curve with that.我想使用CSS 面具,但我不确定是否可以用它制作想要的曲线。


These are my main research points:这些是我的主要研究点:

This is my CodePen and the code这是我的CodePen和代码

 // Position of span that shows range value and tick curve position const tickContainer = document.getElementById('tickContainer'); const range = document.getElementById('range'); const rangeV = document.getElementById('rangeValue'); const setValue = () => { // Span position and inner value const newValue = Number((range.value - range.min) * 100 / (range.max - range.min)); const newPosition = 35 - (newValue * 0.7); rangeV.style.left = `calc(${newValue}% + (${newPosition}px))`; rangeV.innerHTML = `<span>${range.value}%</span>`; // Tick curve position tickContainer.style.setProperty('--p', `calc(${newValue}%)`); }; // Initialize setValue onload and oninput document.addEventListener("DOMContentLoaded", setValue); range.addEventListener('input', setValue);
 body { font-family: Arial; margin: 50px; }.range-wrap { position: relative; } /* Styling of ticks (lines) over the range */.ticks { position: absolute; left: -15px; right: -15px; padding:0 15px; top: -30px; height: 45px; background: repeating-linear-gradient(to right, #D3D3D3 0 1px, transparent 1px 9px); background-clip:content-box; -webkit-mask: radial-gradient(farthest-side at bottom,transparent 75%, #fff 76% 98%, transparent) var(--p) 0px/100px 50px, linear-gradient(#fff, #fff) var(--p) 100%/95px 10px, linear-gradient(#fff, #fff) bottom /100% 10px; -webkit-mask-repeat: no-repeat; -webkit-mask-composite: source-over,destination-out; mask: radial-gradient(farthest-side at bottom,transparent 75%, #fff 76% 98%, transparent) var(--p) 0px/100px 50px, linear-gradient(#fff, #fff) var(--p) 100%/95px 10px, linear-gradient(#fff, #fff) bottom /100% 10px; mask-repeat: no-repeat; mask-composite: exclude; } /* Styling the range */ input[type=range] { -webkit-appearance: none; appearance: none; margin: 20px 0; width: 100%; height: 4px; background-image: linear-gradient(125deg, #e0e0e0 34%, rgb(0,12,110) 100%); outline: none; transition: all 100ms ease; } /* Range track */ input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 4px; cursor: pointer; border-radius: 25px; } input[type=range]::-moz-range-track { width: 100%; height: 4px; cursor: pointer; border-radius: 25px; } /* Range thumb */ input[type=range]::-webkit-slider-thumb { height: 70px; width: 70px; -webkit-transform: translateY(-44.3%) rotate(-45deg); transform: translateY(-44.3%) rotate(-45deg); -webkit-appearance: none; appearance: none; background: #ddd; border: 3px solid transparent; border-color: transparent transparent #fff #fff; border-radius: 50%; cursor: pointer; background-image: linear-gradient(white, white), linear-gradient(to right, #e0e0e0 34%, rgb(0,12,110) 100%); background-attachment: fixed, fixed; background-clip: padding-box, border-box; transition: all 200ms ease; } input[type=range]::-moz-range-thumb { height: 63px; width: 63px; appearance: none; background: #ddd; border: 3px solid transparent; transition: all 200ms ease; border-color: transparent transparent #fff #fff; border-radius: 50%; cursor: pointer; background-image: linear-gradient(white, white), linear-gradient(to right, #e0e0e0 34%, rgb(0,12,110) 100%); background-attachment: fixed, fixed; background-clip: padding-box, border-box; } /* Range value (label) inside of range thumb */.range-value { position: absolute; top: -50%; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center; z-index: 99; user-select: none; select: none; pointer-events: none; }.range-value span { width: 50px; height: 50px; line-height: 50px; text-align: center; color: #fff; background: rgb(0,12,110); font-size: 18px; display: block; position: absolute; top: 20px; border-radius: 50%; user-select: none; select: none; pointer-events: none; z-index: 100; }
 <div class="range-wrap"> <!-- Ticks (lines) over slider --> <div class="ticks" id="tickContainer"> </div> <!-- Range value inside of range thumb --> <div class="range-value" id="rangeValue"></div> <!-- Range itself --> <input id="range" type="range" min="1" max="100" value="5" step="1"> </div>

Here is different idea where I will rely on mask like my previous answer but this time I will introduce and SVG for the curved part.这是不同的想法,我将像以前的答案一样依赖掩码,但这次我将介绍弯曲部分的 SVG。 I will also optimize the code a little to have less of code.我还将稍微优化代码以减少代码。

You will notice that I am using the same mask for both the ticks and the range element but with some different values since the ticks need to have a bigger curve.您会注意到,我对刻度和范围元素使用相同的掩码,但具有一些不同的值,因为刻度需要具有更大的曲线。

 // Position of span that shows range value and tick curve position const tickContainer = document.querySelector('.range-wrap'); const range = document.getElementById('range'); const rangeV = document.getElementById('rangeValue'); const setValue = () => { // Span position and inner value const newValue = Number((range.value - range.min) * 100 / (range.max - range.min)); const newPosition = 30 - (newValue * 0.6); rangeV.style.left = `calc(${newValue}% + (${newPosition}px))`; rangeV.innerHTML = `${range.value}%`; // Tick curve position tickContainer.style.setProperty('--p', `calc(${newValue}%)`); }; // Initialize setValue onload and oninput document.addEventListener("DOMContentLoaded", setValue); range.addEventListener('input', setValue);
 body { font-family: Arial; margin: 50px; }.range-wrap { position: relative; --svg:url("data:image/svg+xml;utf8, <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 15 64 50' preserveAspectRatio='none' ><path d='M0 64 C16 64 16 32 32 32 C48 32 48 64 64 64 L64 48 C52 48 52 16 32 16 C12 16 12 48 0 48 L0 64 Z' fill='white'/></svg>") var(--p,0) 0; } /* Styling of ticks (lines) over the range */.ticks { --sw:120px; /* control the width of the curve */ position: absolute; left: -30px; right: -30px; padding:0 10px; height: 50px; background: repeating-linear-gradient(to right, red 0 3px, transparent 1px 9px) content-box; -webkit-mask:var(--svg) /var(--sw) 50px, linear-gradient(to right, #fff calc(50% - var(--sw)/2 + 1px), transparent 0 calc(50% + var(--sw)/2 - 1px), #fff 0) right var(--p) top 33px/calc(200% - var(--sw)) 16px; -webkit-mask-repeat:no-repeat; z-index:999; } /* Styling the range */ input[type=range] { --sw:100px; /* control the width of the curve */ -webkit-appearance: none; appearance: none; margin: 20px 0 20px -20px; padding:0 20px; width:100%; height: 90px; -webkit-mask: var(--svg) /var(--sw) 50px, linear-gradient(to right, #fff calc(50% - var(--sw)/2 + 1px), transparent 0 calc(50% + var(--sw)/2 - 1px), #fff 0) right var(--p) top 33px/calc(200% - var(--sw)) 16px; -webkit-mask-repeat:no-repeat; background: linear-gradient(125deg, #e0e0e0 34%, rgb(0,12,110) 100%); outline: none; } /* Range track */ input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 50px; cursor: pointer; border-radius: 25px; } input[type=range]::-moz-range-track { width: 100%; height: 50px; cursor: pointer; border-radius: 25px; } /* Range thumb */ input[type=range]::-webkit-slider-thumb { height: 60px; width: 60px; -webkit-appearance: none; appearance: none; border-radius: 50%; cursor: pointer; opacity:0; } input[type=range]::-moz-range-thumb { height: 60px; width: 60px; appearance: none; border-radius: 50%; cursor: pointer; opacity:0; } /* Range value (label) inside of range thumb */.range-value { width: 50px; height: 50px; line-height: 50px; text-align: center; color: #fff; background: rgb(0,12,110); font-size: 18px; position: absolute; transform:translateX(-50%); top: 45px; border-radius: 50%; user-select: none; select: none; pointer-events: none; }
 <div class="range-wrap"> <!-- Ticks (lines) over slider --> <div class="ticks" id="tickContainer"> </div> <!-- Range value inside of range thumb --> <div class="range-value" id="rangeValue"></div> <!-- Range itself --> <input id="range" type="range" min="1" max="100" value="5" step="1"> </div>

UPDATE更新

The final version used by the OP: OP使用的最终版本:

 // Position of span that shows range value and tick curve position const tickContainer = document.querySelector('.range-wrap'); const range = document.getElementById('range'); const rangeV = document.getElementById('rangeValue'); const setValue = () => { // Span position and inner value const newValue = Number((range.value - range.min) * 100 / (range.max - range.min)); const newPosition = 30 - (newValue * 0.6); rangeV.style.left = `calc(${newValue}% + (${newPosition}px))`; rangeV.innerHTML = `${range.value}%`; // Tick curve position tickContainer.style.setProperty('--p', `calc(${newValue}%)`); }; // Initialize setValue onload and oninput document.addEventListener("DOMContentLoaded", setValue); range.addEventListener('input', setValue);
 body { font-family: Arial; margin: 0; min-height: 100vh; padding: 50px; box-sizing: border-box; text-align:center; }.range-wrap { position: relative; --svg:url("data:image/svg+xml;utf8, <svg width='97' height='37' viewBox='0 1.5 97 37' xmlns='http://www.w3.org/2000/svg'><path d='M0 35C14 35 13 2 48.5 2C84 2 80.5 35 97 35' fill='none' stroke='white' stroke-width='4'/></svg>") var(--p,0) 0; </svg> } /* Styling of ticks (lines) over the range */.ticks { --sw:120px; /* control the width of the curve */ position: absolute; left: -30px; right: -30px; top: 0px; padding:0 10px; height: 50px; background: repeating-linear-gradient(to right, #D3D3D3 0 1px, transparent 1px 10px) content-box; -webkit-mask:var(--svg) /var(--sw) 50px, linear-gradient(to right, #fff calc(50% - var(--sw)/2 + 1px), transparent 0 calc(50% + var(--sw)/2 - 1px), #fff 0) right var(--p) top 38px/calc(200% - var(--sw)) 6px; -webkit-mask-repeat:no-repeat; z-index:999; } /* Styling the range */ input[type=range] { --sw:100px; /* control the width of the curve */ -webkit-appearance: none; appearance: none; margin: 20px 0 20px -20px; padding:0 20px; width: 100%; height: 60px; -webkit-mask: var(--svg) /var(--sw) 41px, linear-gradient(to right, #fff calc(50% - var(--sw)/2 + 1px), transparent 0 calc(50% + var(--sw)/2 - 1px), #fff 0) right var(--p) top 34.45px/calc(200% - var(--sw)) 4px; -webkit-mask-repeat:no-repeat; background: linear-gradient(125deg, #e0e0e0 34%, rgb(0,12,110) 100%); outline: none; } /* Range track */ input[type=range]::-webkit-slider-runnable-track { width: 100%; height: 50px; cursor: pointer; border-radius: 25px; } input[type=range]::-moz-range-track { width: 100%; height: 50px; cursor: pointer; border-radius: 25px; } /* Range thumb */ input[type=range]::-webkit-slider-thumb { height: 60px; width: 60px; -webkit-appearance: none; appearance: none; border-radius: 50%; cursor: pointer; opacity:0; } input[type=range]::-moz-range-thumb { height: 60px; width: 60px; appearance: none; border-radius: 50%; cursor: pointer; opacity:0; } /* Range value (label) inside of range thumb */.range-value { width: 55px; height: 55px; line-height: 60px; text-align: center; color: #fff; background: rgb(0,12,110); font-size: 18px; position: absolute; transform:translateX(-50%); top: 32px; border-radius: 50%; user-select: none; select: none; pointer-events: none; }
 <h2>Custom range slider with ticks</h2> <div class="range-wrap"> <!-- Ticks (lines) over slider --> <div class="ticks" id="tickContainer"> </div> <!-- Range value inside of range thumb --> <div class="range-value" id="rangeValue"></div> <!-- Range itself --> <input id="range" type="range" min="1" max="100" value="5" step="1"> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM