нужна помощь в реализации элемента интерфейса. Решил я реализовать 1 элемент, скрин его вы увидите ниже, и что-то понятия не имею как такое реализуется. Начал писать на и в принципе что-то получилось, но далекое от идеала. Итак, нужно чтобы в месте где стоит синий ползунок и пересекаются обе линии, стоял ползунок способный двигать их, т.е. если повернуть им влево, то красная полоса уменьшиться, а если вправо, то уменьшиться зеленая, и увеличится другая, думаю вы поняли, и мне этот момент совершенно непонятен, как такое можно реализовать? Объединить 2 ? Но как это сделать, чтобы на них можно было воздействовать ползунком?
Если это можно как-то реализовать не прибегая к svg буду очень рад помощи, если в примере будет js и вам будет не сложно, то очень хотелось бы увидеть нативный, т.к. изучаю его параллельно.
Мой код:
.grey-cricle {
-webkit-transform: rotate(-85deg) translate(-282px, 16px);
-ms-transform: rotate(-85deg) translate(-282px, 16px);
-o-transform: rotate(-85deg) translate(-282px, 16px);
transform: rotate(-85deg) translate(-282px, 16px);
}
.new-game {
width: 400px;
}
<svg viewBox="0 0 330 275" class="new-game">
<text x="135" y="20" font-family="Arial" font-size="10" text-anchor="middle" fill="#A9A8A8">100</text>
<text x="165" y="20" font-family="Arial" font-size="10" text-anchor="middle" fill="#A9A8A8">0</text>
<circle r="120" cx="150" cy="145" fill="none" stroke-width="3" stroke="#ccc" stroke-dasharray="740" class="grey-cricle" />
<circle r="100" cx="150" cy="145" fill="none" stroke-width="14" stroke="#ccc" />
<circle r="100" cx="150" cy="145" stroke-width="8" stroke="red" stroke-dasharray="250 502"
stroke-dashoffset="0" fill="none" stroke-linecap="butt"/>
<circle r="30" cx="150" cy="145" fill="#006EFA" stroke="url(#gradient-game)" />
<text x="150" y="150" fill="#fff" font-size="16" text-anchor="middle">99.99</text>
</svg>
На мой взгляд такие вещи нужно писать на фреймворках, но можно и на ванильном JS. Остались мелкие баги, но они устраняются уже элементарно и больше подвязаны к разметке svg.
У нас есть 5 эвентов: на нажатие клавиши(только движок), на отжатие клавиши (вся область svg), покидание мышью области svg, и движение мыши.
При клике мы вычисляем абсолютный центр надписи (нужно для определения угла).
При движении мыши мы определяем текущий угол поворота регулятора и отрисовываем его в svg.
Сделал еще параллельный event для обработки скрола мышкой или тачпадом - тоже удобно пользователю.
Если есть вопросы по коду - спрашивайте.
function bind(func, context) {
return function() { // (*)
return func.apply(context, arguments);
};
}
function getPos(el) {
var rect = el.getBoundingClientRect();
console.log(rect);
return {
x: rect.left + rect.width / 2.0,
y: rect.top + rect.height / 2.0
};
}
var radius = parseFloat(stroker.getAttribute("r"));
var curObj = {
draggin: false,
dragX: 0,
dragY: 0,
cent: {
x: 0,
y: 0
},
onMDTasker: function(evt) {
this.draggin = true;
this.cent = getPos(this.text);
console.log(this.cent);
this.dragX = evt.clientX;
this.dragY = evt.clientY;
this.dragFi = 2 * Math.PI / 100.0 * this.value * 0.98;
return false;
},
onMUTasker: function(evt) {
this.draggin = false;
return false;
},
onMMTasker: function(evt) {
if (this.draggin) {
var curX = evt.clientX;
var curY = evt.clientY;
var deltaX = curX - this.cent.x;
var deltaY = curY - this.cent.y;
var Ksi = Math.atan2(deltaY, deltaX);
var correctedKsi = Ksi + Math.PI / 2.0 * 0.98;
if (correctedKsi < 0) { correctedKsi = correctedKsi + 2 * Math.PI; }
var newVal = correctedKsi / 2 / Math.PI * 100.0 / 0.98;
if (newVal < 0) { newVal = 0.0; }
if (newVal > 100) { newVal = 100.0; }
this.value = newVal;
this.applyOutState();
}
return false;
},
onWheelEvent: function(evt) {
var deltaY = evt.deltaY;
var oldValue = parseFloat(this.text.innerHTML);
var newValue = oldValue + deltaY / 10.0;
if (newValue < 0) { newValue = 0.0; }
if (newValue > 100) { newValue = 100.0; }
this.value = newValue;
this.applyOutState();
return false;
},
svgE: document.getElementsByClassName("new-game")[0],
value: 50.00,
circLen: radius * 2 * Math.PI,
applyOutState: function() {
this.text.innerHTML = this.value.toFixed(2);
var fi = 2 * Math.PI / 100.0 * this.value * 0.98;
var circleArcLen = fi * radius;
var empteArcLen = this.circLen - circleArcLen;
this.stroker.setAttribute("stroke-dashoffset", "148");
this.stroker.setAttribute("stroke-dasharray", circleArcLen + " " + empteArcLen);
this.tasker.setAttribute("cx", 150 + 95 * Math.cos(fi - Math.PI / 2.0 * 0.98))
this.tasker.setAttribute("cy", 150 + 95 * Math.sin(fi - Math.PI / 2.0 * 0.98))
}
}
curObj.onWheelEvent = bind(curObj.onWheelEvent, curObj);
curObj.onMUTasker = bind(curObj.onMUTasker, curObj);
curObj.onMDTasker = bind(curObj.onMDTasker, curObj);
curObj.onMMTasker = bind(curObj.onMMTasker, curObj);
curObj.svgE.addEventListener("wheel", curObj.onWheelEvent);
curObj.text = document.getElementById("textVal");
curObj.stroker = document.getElementById("stroker");
curObj.tasker = document.getElementById("tasker");
curObj.tasker.addEventListener("mousedown", curObj.onMDTasker);
curObj.svgE.addEventListener("mouseup", curObj.onMUTasker);
curObj.svgE.addEventListener("mousemove", curObj.onMMTasker);
curObj.svgE.addEventListener("mouseleave", curObj.onMUTasker);
curObj.applyOutState();
<svg viewBox="0 0 330 275" class="new-game">
<text x="135" y="20" font-family="Arial" font-size="10" text-anchor="middle" fill="#A9A8A8">100</text>
<text x="165" y="20" font-family="Arial" font-size="10" text-anchor="middle" fill="#A9A8A8">0</text>
<circle r="120" cx="150" cy="145" fill="none" stroke-width="3" stroke="#ccc" stroke-dasharray="740" class="grey-cricle" />
<circle r="100" cx="150" cy="145" fill="none" stroke-width="14" stroke="#ccc" />
<circle r="100" cx="150" cy="145" stroke-width="8" stroke="green" stroke-dasharray="614 14" stroke-dashoffset="148" fill="none" stroke-linecap="butt" />
<circle id="stroker" r="100" cx="150" cy="145" stroke-width="8" stroke="red" stroke-dasharray="250 502" stroke-dashoffset="0" fill="none" stroke-linecap="butt" />
<circle r="30" cx="150" cy="145" fill="#006EFA" stroke="url(#gradient-game)" />
<text x="150" y="150" fill="#fff" font-size="16" text-anchor="middle" id="textVal">99.99</text>
<circle id="tasker" r="10" cx="200" cy="200" fill="#0000aa" stroke="gold" stroke-width="2" />
</svg>
Используетсья плагин RoundSlider
Цвета правильные не смог подобрать но думаю такого примера впольне хватит.
let fn1 = $.fn.roundSlider.prototype._setProperties;
$.fn.roundSlider.prototype._setProperties = function () {
fn1.apply(this);
let o = this.options, r = o.radius, d = r * 2,
r1 = r - (o.width / 2) - this._border(true),
svgNS = "http://www.w3.org/2000/svg";
this._circum = Math.PI * (r1 * 2);
let $svg = $(document.createElementNS(svgNS, "svg"));
$svg.attr({ "height": d, "width": d });
this.$circle = $(document.createElementNS(svgNS, 'circle')).attr({
"fill": "transparent", "class": "rs-transition",
"cx": r,
"cy": r,
"r": r1,
"stroke-width": o.width - 2,
"stroke-dasharray": this._circum
});
let $path = this.$circle.clone().addClass("path-bg");
this._setDashOffset($path, this._end);
// ####---- Добавление border ----####
let $border = this.$circle.clone().addClass("path-border").attr({
"stroke-width": o.width
});
this._setDashOffset($border, this._end + 1.5);
let $border_wrapper = $(document.createElementNS(svgNS, 'g')).css({
"transform-origin": "50% 50%",
"transform": "rotate(-1deg)"
});
$border_wrapper.append($border);
$svg.append($border_wrapper, $path, this.$circle.addClass("range-bg"));
this.$svg_box = $(document.createElement("div")).addClass("rs-transition rs-svg").append($svg).css({
"height": d,
"width": d,
"transform-origin": "50% 50%",
"transform": "rotate(" + (o.startAngle + 180) + "deg)"
}).appendTo(this.innerContainer);
}
$.fn.roundSlider.prototype._setDashOffset = function ($ele, deg) {
let pct = (1 - (deg / 360)) * this._circum;
$ele.css({ strokeDashoffset: pct });
}
let fn2 = $.fn.roundSlider.prototype._changeSliderValue;
$.fn.roundSlider.prototype._changeSliderValue = function (val, deg) {
fn2.apply(this, arguments);
deg = deg - this.options.startAngle;
if (this._rangeSlider) {
this.$svg_box.rsRotate(this._handle1.angle + 180);
deg = this._handle2.angle - this._handle1.angle;
}
this._setDashOffset(this.$circle, deg);
}
/// настройки самого плагина ///
$("#slider").roundSlider({
sliderType: "min-range",
handleShape: "dot",
radius: 110,
startAngle: 95,
endAngle: "+350",
min: 3,
max: 97,
width: 16,
handleSize: "+10",
create: function (event) {
this.control.find(".rs-handle").addClass("rs-transition").eq(0).rsRotate(-this._handle1.angle);
}
}).on("change drag", function (event) {
$(event.handle.element).rsRotate(-event.handle.angle);
});
$("#slider").roundSlider("option", "value", 50);
.rs-border {
border: none !important;
}
.rs-control .rs-range-color,
.rs-control .rs-path-color,
.rs-control .rs-bg-color {
background-color: transparent;
}
.rs-control circle.path-bg {
stroke: red;
}
.rs-control circle.range-bg {
stroke: #81ce00;
}
.rs-control circle.path-border {
stroke: #aaa;
}
.full .rs-tooltip {
top: 50%;
left: 50%;
background: skyblue !important;
color:#fff !important;
border-radius:50%;
}
.rs-handle-dot:after {
display: flex !important;
content: "\f104 \f105" !important;
font-family: FontAwesome !important;
height: 100% !important;
justify-content: space-around;
padding: 5px !important;
align-items: center !important;
width: 100% !important;
border-radius: 1000px;
color: #fff !important;
}
.rs-handle-dot {
background-color: skyblue !important;
}
.rs-handle-dot:after {
background-color: skyblue !important;
border:none !important;
}
.rs-handle-dot {
border: none !important;
padding: 0px !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css" rel="stylesheet"/>
<div id="slider"></div>
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости