我想创建一个圆形的玩家搜索栏。我必须有两个手柄。一个是缓冲部分(图片中的灰色),它不是拖动,而是更多可拖动的项目,表示正在播放的歌曲的当前部分。我不确定如何实现这一目标。看起来像帆布是最好的选择。但我不确定是否可以在画布中拖动物品。
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 100;
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
context.lineWidth = 25;
context.strokeStyle = '#003300';
context.stroke();
如何实现它的任何想法或例子?
答案 0 :(得分:0)
圈控
这个答案显示了如何创建游戏类型控件。它以固定间隔运行,每隔一段时间渲染一次。它不是事件驱动的,鼠标事件不调用控件,控件只是检查当前鼠标位置。如果间隔低于鼠标事件间隔,则某些鼠标移动将丢失,但控件也可以正常工作。
通过创建自己的鼠标事件监听器并在需要时调用update
和draw()
,可以轻松地将此控件调整为事件驱动。警告虽然此控件使用自定义鼠标对象来更改光标并管理哪个控件具有鼠标的所有权。鼠标需要所有权,以便当您将一个控制柄拖到另一个控件上时,您不会开始拖动另一个控件。它的工作原理是将mouse.mousePrivate
值设置为控件唯一ID,如果mouse.mousePrivate
不等于0
(无所有者)或控件的ID
请参阅代码了解其工作原理。该演示显示了几个控件,显示了不同的行为。中心控制是一个简单的范围控制,从0到100,左上角有10个旋转,范围从0到1000,并显示两个位置弧来显示位置。右上方控件具有浮动值,即如果您正在寻找视频,则搜索位置不会立即响应,因此控件具有浮动值,该位置显示位置弧作为实际位置,而数字和拨号手柄显示实际位置
这只是一个例子,您应该只需要你想要的东西。它尚未在多个浏览器上进行测试,除了最基本的测试之外没有任何其他测试,所以不要按原样使用它。
demo = function(){
/** fullScreenCanvas.js begin **/
var canvas = (function(){
var canvas = document.getElementById("canv");
if(canvas !== null){
document.body.removeChild(canvas);
}
// creates a blank image with 2d context
canvas = document.createElement("canvas");
canvas.id = "canv";
canvas.style.background = "#AAA";
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = "absolute";
canvas.style.top = "0px";
canvas.style.left = "0px";
canvas.style.zIndex = 1000;
canvas.ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
return canvas;
})();
var ctx = canvas.ctx;
/** fullScreenCanvas.js end **/
/** MouseFull.js begin **/
var canvasMouseCallBack = undefined; // if needed
var mouse = (function(){
function cursorControl(cursor){
if(cursor !== this.lastCursor){
if(cursor !== "default"){
this.lastCursor = cursor;
}
}
}
function update(){
if(this.element !== undefined){
this.element.style.cursor = this.lastCursor;
this.lastCursor = "default";
}
}
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false,
interfaceId : 1, buttonLastRaw : 0, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
getInterfaceId : function () { return this.interfaceId++; }, // For UI functions
mousePrivate : 0,
lastCursor: "default",
setCursor : cursorControl,
frameEnd : update,
startMouse:undefined,
element : undefined,
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey;m.shift = e.shiftKey;m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") { m.buttonRaw = 0; m.over = false;
} else if (t === "mouseover") { m.over = true;
} else if (t === "mousewheel") { m.w = e.wheelDelta;
} else if (t === "DOMMouseScroll") { m.w = -e.detail;}
if (canvasMouseCallBack) { canvasMouseCallBack(m.x, m.y); }
e.preventDefault();
}
function startMouse(element){
if(element === undefined){
element = document;
}
mouse.element = element;
"mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",").forEach(
function(n){element.addEventListener(n, mouseMove);});
element.addEventListener("contextmenu", function (e) {e.preventDefault();}, false);
}
mouse.mouseStart = startMouse;
return mouse;
})();
if(typeof canvas !== "undefined"){
mouse.mouseStart(canvas);
}else{
mouse.mouseStart();
}
/** MouseFull.js end **/
/** ImageTools.js begin **/
var imageTools = (function(){
var iT = {
canvas:function(w,h){var c=document.createElement("canvas");c.width=w;c.height=h;return c;},
createImage:function(w,h){var i=iT.canvas(w,h);i.ctx=i.getContext("2d");return i;},
loadImage:function(url,cb){var i=new Image();i.src=url;i.addEventListener('load',cb);return i;},
image2Canvas:function(ig){var i=iT.canvas(ig.width,ig.height);i.ctx=i.getContext("2d");i.drawImage(ig,0,0);return i;},
imageData:function(img){return (img.ctx||(iT.image2Canvas(img).ctx)).getImageData(0,0,img.width,img.height).data;},
saveAsPNG:function(image,filename){ // No IE <11 support. Chrome URL bug for large files may crash
var ac,e;
if(image.toDataURL === undefined){ image = it.image2Canvas(image);}
ac = document.createElement('a'); ac.href = image.toDataURL(); ac.download = filename+".png";
// need to update this as it has depreciated.
if (document.createEvent) {
(e = document.createEvent("MouseEvents")).initMouseEvent("click", true, true, window,0, 0, 0, 0, 0, false, false, false,false, 0, null);
ac.dispatchEvent(e);
}
}
};
return iT;
})();
/** ImageTools.js end **/
var w = canvas.width;
var h = canvas.height;
var ix = Math.ceil(Math.min(w, h) / 20);
const PI2 = Math.PI * 2;
// following three function are for drawing, updating, and on to set floating value
// draw circular control needs to be bound to a circular control object
function drawCirControl(){
var c, r, r1, r2, x, y, w, a;
c = this.ctx;
r = this.radius1;
r1 = this.radius2;
c.lineWidth = w = Math.abs(r - r1);
r2 = Math.min(r, r1) + w / 2;
c.strokeStyle = this.borderColour;
c.beginPath();
c.arc(this.x, this.y, r2, 0, Math.PI * 2)
c.stroke();
c.strokeStyle = this.colour;
c.lineWidth = w - this.border * 2;
c.beginPath();
c.arc(this.x, this.y, r2, 0, Math.PI * 2)
c.stroke();
// if more than one turn between min and max then also display progress as one turn
if(Math.abs(this.startAng - this.endAng) > PI2){
c.lineCap = "round";
c.strokeStyle = this.lineColour;
c.lineWidth = (w - this.border * 6)/2;
c.beginPath();
a = (this.floatingRaw - this.startAng) % PI2 + this.startAng;
c.arc(this.x, this.y, r2 + (w - this.border * 6)/4, this.startAng, a)
c.stroke();
a = this.startAng + ((this.floatingValue - this.min) / (this.max - this.min)) * PI2;
c.lineCap = "round";
c.strokeStyle = this.lineInnerColour;
c.lineWidth = (w - this.border * 6)/2;
c.beginPath();
c.arc(this.x, this.y, r2 - (w - this.border * 6)/4, this.startAng, a)
c.stroke();
}else{
c.lineCap = "round";
c.strokeStyle = this.lineColour;
c.lineWidth = w - this.border * 6;
c.beginPath();
c.arc(this.x, this.y, r2, this.startAng, this.floatingRaw)
c.stroke();
}
x = Math.cos(this.raw) * r2 + this.x;
y = Math.sin(this.raw) * r2 + this.y;
c.lineWidth = this.border;
c.strokeStyle = this.borderColour;
c.fillStyle = this.colour;
c.beginPath();
c.arc(x, y, (w * this.handleSize)/2, 0, Math.PI * 2);
c.fill();
c.stroke();
c.font = this.font;
c.textAlign = "center";
c.textBaseline = "middle";
c.fillText(Math.round(this.value), this.x, this.y);
}
// Update by checking mouse position and setting cursor and controling the dragging
// circular control needs to be bound to a circular control object
function updateCircularControl(){
var r, r1, r2, x, y, dist, ang, a, w, h, mouseOver;
r = this.radius1;
r1 = this.radius2;
w = Math.abs(r - r1);
r2 = Math.min(r, r1) + w / 2;
if(!this.floating){
this.floatingValue = this.value;
}
this.raw = (this.value / (this.max - this.min)) * (this.endAng - this.startAng) + this.startAng;
this.floatingRaw = (this.floatingValue / (this.max - this.min)) * (this.endAng - this.startAng) + this.startAng;
x = Math.cos(this.raw) * r2 + this.x;
y = Math.sin(this.raw) * r2 + this.y;
mouseOver = false;
dist = Math.sqrt(Math.pow(x - mouse.x, 2) + Math.pow(y - mouse.y, 2));
if(this.mouse.mousePrivate === 0 || this.mouse.mousePrivate === this.id){
if(dist < w * this.handleSize || this.dragging){
this.mouse.setCursor("pointer");
mouseOver = true;
this.mouse.mousePrivate = this.id;
if((this.mouse.buttonRaw & 1) === 1 && !this.dragging){
this.dragging = true;
this.lastAng = (this.raw + PI2) % PI2;
}else{
if((this.mouse.buttonRaw & 1) === 0){
this.dragging = false;
}else{
// get the angle to the mouse
ang = ((Math.atan2(mouse.y - this.y, mouse.x - this.x)) + PI2) % PI2;
// get the delta from last angle
a = (ang - this.lastAng);
// check that is has not cycled and adjust acordingly
if(a < -Math.PI * (3/2)){
a += PI2
}
if(a > Math.PI * (3/2)){
a -= PI2
}
// set last angle
this.lastAng = ang;
// set the raw vale
this.raw += a;
this.value = ((this.raw - this.startAng) / (this.endAng - this.startAng)) * (this.max - this.min) + this.min
this.value = Math.min(this.max, Math.max(this.min, this.value));
if(!this.floating){
this.floatingValue = this.value;
}
// recalculate the raw value
this.raw = (this.value / (this.max - this.min)) * (this.endAng - this.startAng) + this.startAng;
this.floatingRaw = (this.floatingValue / (this.max - this.min)) * (this.endAng - this.startAng) + this.startAng;
}
}
}
}
if(! mouseOver && this.mouse.mousePrivate === this.id ){
this.mouse.mousePrivate = 0;
}
}
// set circular control floating value needs to be bound to a circular control object
function setCirciularFloatingValue(v){
this.floatingValue = v;
this.floatingRaw = (this.floatingValue / (this.max - this.min)) * (this.endAng - this.startAng) + this.startAng;
}
// create a circular control
// x, y is the center pos;
// min max is the min max values
// r1 r2 are inner and outer radius
// ctx is the canvas context to draw to
// mouse is the mouse object. This is a custom mouse object provided
// in the demo.
function createCircularControl(x,y,min,max,r1,r2,ctx,mouse){
var s, fontSize;
s = Math.min(r1,r2);
fontSize = Math.ceil(s/2);
var control = {
raw : 0, // the raw angle to draw the control handle ar
floatingRaw : 0, // if the actuale value floats independent of control position
lastAng : 0,
x : x, // center of control
y : y,
min : min, // min and max values
max : max,
value : 0, // value of control
floatingValue : 0,
startAng : -Math.PI/2, // start angle
endAng : Math.PI * (3/2), // end angle
radius1 : r1, // inner and outer angles
radius2 : r2,
border : 1, // border radius
colour : "white", // inner colour
borderColour : "black", // border colour
lineColour : "#5AF",
lineInnerColour : "#5AF",
font : fontSize + "px Arial Black", // font for center display
handleSize : 1.5, // handle size aas ratio to control width
floating : false,
draw : drawCirControl, // function to draw control
ctx : ctx, // get the context to draw to
mouse : mouse, // set the mouse
id : mouse.getInterfaceId(), // get an ID for this control
update : updateCircularControl, // updates the control
setFloatingValue : setCirciularFloatingValue,
}
return control;
}
// create the controls
var cont = createCircularControl(w/2, h/2, 0, 100, ix*5, ix*6, ctx, mouse);
var cont1 = createCircularControl(w/6, h/4, 0, 1000, ix*2, ix*3, ctx, mouse);
var cont2 = createCircularControl(w - w/6, h/4, 0, 1000, ix*2, ix*3, ctx, mouse);
// set up top left control extra attributes
// It has 10 rotations between min and max
cont1.startAng = -Math.PI/2;
cont1.endAng = -Math.PI/2 + PI2 * 10;
cont1.handleSize = 1.0;
cont1.lineInnerColour = "#8C5";
// set up top left control extra attributes
cont2.startAng = 0;
cont2.endAng = PI2 * 5;
cont2.handleSize = 1.5;
cont2.lineInnerColour = "#C85";
cont2.colour = "black";
cont2.borderColour = "Green";
cont2.border = 3;
cont2.floating = true;
var fValue = 0;
var fdValue = 0
// Updates all
function update(){
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0, 0, w, h);
fdValue += (cont2.value - fValue) * 0.01;
fdValue *= 0.6;
fValue += fdValue;
cont2.setFloatingValue(fValue);
cont.update();
cont1.update();
cont2.update();
cont.draw();
cont1.draw();
cont2.draw();
if(!STOP){
requestAnimationFrame(update);
}else{
var canv = document.getElementById("canv");
if(canv !== null){
document.body.removeChild(canv);
}
STOP = false;
}
mouse.frameEnd();
}
update();
}
var STOP = false;
function resizeEvent(){
var waitForStopped = function(){
if(!STOP){ // wait for stop to return to false
demo();
return;
}
setTimeout(waitForStopped,200);
}
STOP = true;
setTimeout(waitForStopped,100);
}
window.addEventListener("resize",resizeEvent);
demo();