There is a need
There is a need, shake the phone to move the ball to give the cat an edge.
Pull a bunch of people to evaluate, see how to do it.
See this demand is really leng next, and then carefully think about it, it seems that there is no way.
A feature that had not been used for a long time came to mind, “gyroscope”.
What is a gyroscope
It’s a directional sensor that picks up data when it detects a change in direction.
A change of direction event is an event triggered when a mobile device, such as a mobile phone, reverses direction.
There’s a lot you can do with gyroscope on mobile. He has an API in our Web.
The Window object can listen for DeviceOrientation events to call the gyroscope sensor on the mobile.
But we mainly use it to judge the direction in this paper.
Just stroke the cat. Rules – only shake the phone
Analysis of a wave
First, shake the phone and move the brush stroke. The fewer words, the bigger the matter.
We can get direction with the gyroscope, but how do we stroke?
Can we draw a path beforehand and then move the brush to draw a path?
It looks like it could, but here’s the thing,
So how do we plot this path?
So how do we store this path?
How can you tell which way a path is moving by shaking your phone?
Practice is the only criterion for testing code
Let’s start by creating a sketchpad tool that can manually stroke the cat.
Create a simple printboard tool
First, we need an artboard to draw the stroke path.
Here I use React + Pixijs to build the artboard.
I’m not going to go into the structure of the project, just get started. Let’s take a look at the finished drawing board.
Function introduction:
- Set the brush width β
- Set the brush color β
- Draw base map support network picture and local picture drag β
- Display and copy the path array β
- Undo β
- Remove β
- Demo β
Set the stage
Use React to build the page and canvas stage. We need to draw paths on the stage.
Interested students can view the source code
Here I used a 750*1624 stage size for mobile.
/** Initialize the stage */
onInitStage = () = > {
const _c = this.stageCancas.current;
// Width and height of design draft
const width = 750;
const height = 1624;
const app = new PIXI.Application(width, height, {
view: _c,
backgroundColor: 0x1099bb
});
app.ticker.add((deltaTime) = >{});this.stage = app.stage;
/ / reproduction
const barrel = this.stage.addChild(PIXI.Sprite.from("./barrel.png"));
barrel.interactive = true;
this.barrel = barrel;
/ / the ball
this.ball = this.stage.addChild(PIXI.Sprite.from("./ball.png"));
this.ball.anchor.set(this.ball.width / 2.this.ball.height / 2)
this.onAddEvents(barrel);
/ / draw the line
this.line = new PIXI.Graphics();
this.stage.addChild(this.line);
}
Copy the code
Draw paths on the canvas
Draw different paths on the canvas by listening for mouse press, move, and lift events.
Record the first stroke of the first step when the mouse is pressed down.
this.line.lineStyle(this.state.lineWidth, this.state.color.replace("#"."0x"), 1);
this.line.moveTo(_x, _y);
// First point
this.currentPoints = [];
this.currentPoints.push({ x: _x, y: _y });
Copy the code
As the mouse moves, each point is stored. Here we can set sparse or dense collection points.
// Whether to draw points
let disX = Math.abs(point.x - this.lastPoint.x);
let disY = Math.abs(point.y - this.lastPoint.y);
let dis = Math.sqrt(disX * disX + disY * disY);
if (dis >= 2) {
this.line.lineStyle(this.state.lineWidth, this.state.color.replace("#"."0x"), 1);
this.line.lineTo(point.x, point.y);
this.lastPoint = point;
this.currentPoints.push({
x: Math.floor(point.x),
y: Math.floor(point.y)
});
}
Copy the code
When the mouse is up, we are finished drawing the one-step command, storing all the points of the one-step drawing command.
/ / every step
this.step++;
this.stepPoints[this.step] = this.currentPoints;
Copy the code
Then output all the points of all the drawing steps and display them in the code tag.
/** outputs the array */
onGetOutPutArr = () = > {
let outArr = [];
for (let item in this.stepPoints) {
outArr = [...outArr, ...this.stepPoints[item]]
}
this.setState({
listTextArea: outArr.length > 0 ? JSON.stringify(outArr) : "Path array..."
});
}
Copy the code
This way, we can use the mouse to draw on the palette, and each path is recorded.
It is convenient for us to draw in the formal project later.
Add some functionality to the artboard
- Add stroke width
- Add stroke color
state = {
listTextArea: "Path array...".color: "#ffffff".// Brush color
lineWidth: 8 // Brush width
}
Copy the code
Here I use react-color to build the color picker.
import { ChromePicker } from 'react-color';
Copy the code
It worked really well.
- Add the replication path function
Replication is implemented through the execCommand function.
<input type="text" readOnly="readonly" ref={this._select} value={listTextArea} />
Copy the code
this._select.current.select();
document.execCommand("Copy");
Copy the code
- Add undo
Command revocation is controlled through the control step step.
Delete the corresponding step in the object, and then redraw the path using the brush.
/** ζ€ι */
onUndoLastStep = () = > {
delete this.stepPoints[this.step];
this.step--;
// Redraw
this.line.clear();
for (let item in this.stepPoints) {
let lineList = this.stepPoints[item];
if (lineList && lineList.length > 0) {
this.line.lineStyle(this.state.lineWidth, this.state.color.replace("#"."0x"), 1);
this.line.moveTo(lineList[0].x, lineList[0].y);
lineList.forEach(it= > {
this.line.lineTo(it.x, it.y); }}})this.onGetOutPutArr();
}
Copy the code
- Add clear functionality
Use the clear() method directly here.
Remember the cleanup steps and coordinate points.
/** ζΈ
ι€ */
onClickClear = () = > {
this.line.clear();
this.stepPoints = {};
this.currentPoints = [];
this.onGetOutPutArr();
}
Copy the code
- Add demo features
So here I have a little ball going along the path.
/ * * * / demonstration
onShowDemo = () = > {
let count = 0;
this.stage.addChild(this.ball);
let outArr = [];
for (let item in this.stepPoints) {
outArr = [...outArr, ...this.stepPoints[item]]
}
let timer = setInterval(() = > {
move();
}, 30);
/ / move
const move = () = > {
count++;
if (count >= outArr.length) {
clearInterval(timer);
return;
}
this.ball.position.set(outArr[count].x, outArr[count].y)
}
}
Copy the code
- Add drag and drop local image function
So here we use onDragEnter onDragOver onDrop, these three trigger events.
After dragging the local image onto the artboard, we render the image onto the stage.
/** Process the rendered image file */
processFiles = (files) = > {
var file = files[0];
var reader = new FileReader();
let self = this;
reader.onload = function (e) {
let bg = new Image();
bg.src = e.target.result;
/ / to draw
self.barrel.texture = PIXI.Texture.from(bg);
};
// Read the image
reader.readAsDataURL(file);
}
Copy the code
So we have our sketchboard.
So let’s see what happens. I wrote “cat” on the board.
Mobile development
Foreplay is too long. Next comes the main requirement.
Start by drawing a background image with a cat on it.
The demand requires us to shake the phone, move the ball and stroke the cat.
To illustrate, I’ll draw a dashed line for the path.
this.realLine = new PIXI.Graphics();
this.realLine.lineStyle(8.0xf4813e.1);
this.realLine.moveTo(firstList[0].x, firstList[0].y);
app.stage.addChild(this.realLine);
firstList.forEach((item, index) = > {
if (index % 3= =0) {
if (index > 1)
this.line.moveTo(firstList[index - 1].x, firstList[index - 1].y)
this.line.lineTo(item.x, item.y); }});Copy the code
Then draw a ball.
And then we’re going to move this ball around.
Use a gyroscope
Use the gyroscope in the mobile terminal H5 page to obtain the movement direction of the ball.
Note that gyroscope calls in ios require authorization. We also need to click to trigger the authorization popover.
The following is a compatible way of writing a gyroscope license:
// iOS 13+
if (window.DeviceOrientationEvent ! = =undefined && typeof window.DeviceOrientationEvent.requestPermission === 'function') {
window.DeviceOrientationEvent.requestPermission()
.then(function (response) {
if (response == 'granted') {
this.onTestGyro();
}else{
}
}).catch(function (error) {
console.log("error", error);
});
} else {
console.log("Start")
this.onTestGyro();
}
Copy the code
Once the authorization is successful, we can get the direction data.
Notice that the gyroscope returns three parameters: alpha rotates about the z axis, beta rotates about the x axis, and Gamma rotates about the y axis.
I’m only dealing with four directions here.
/** direction right upper left upper right lower left lower */
const DIR = {
RT: "RT".LT: "LT".LB: "LB".RB: "RB"
}
Copy the code
Direction is confirmed by beta and gamma, since only planar motion is required, not 3D motion. So select only beta and gamma.
When e.eta is in the range [-180,0], the direction is top, otherwise the direction is bottom;
Where LLDB amma is in the range [-180,0], the direction is left; otherwise, it is right.
Use the code to determine the following:
window.addEventListener("deviceorientation".(e) = > {
let _dir = "";
if (e.beta >= -180 && e.beta <= 0) {
_dir = _dir + "T";
} else {
_dir = _dir + "B";
}
if (e.gamma >= -180 && e.gamma <= 0) {
_dir = "L" + _dir;
} else {
_dir = "R" + _dir;
}
/ / the console. The info (" gyroscope, "` x: ${e.a lpha}, y: ${e.b eta}, z: ${um participant amma} `, _dir);
this.curerentDir = DIR[_dir];
}, false);
Copy the code
So you can get the direction change in real time.
Move the ball to stroke the cat
So how are we going to move the ball, what we’re going to do here is make our path array a two-way circular list.
/ / list
var Node = function (element) {
this.element = element;
this.next = null;
this.prev = null;
};
Copy the code
Add a method to the list where you can add elements:
this.append = function (element) {
var node = new Node(element),
current,
previous;
if(! head) { head = node; tail = node; head.prev = tail; tail.next = head; }else {
current = head;
while(current.next ! == head) { previous = current; current = current.next; } current.next = node; node.next = head; node.prev = current; }; head.prev = node; length++;return true;
};
Copy the code
After implementing a bidirectional circular list, we convert stroke paths to lists.
/** Convert coordinate array to bidirectional list */
dealArr2List = () = > {
const arr = firstList;
const list = new get2LoopList();
arr.forEach((item, index) = > {
list.append(item);
});
this.currentPoint = list.getHead();
}
Copy the code
It has the current point, the previous point, and the next point. We change the position of the ball every frame.
At the same time, the direction of the previous point and the next point relative to the current point can be obtained by comparing the points.
If the direction is consistent with the direction obtained by the gyroscope, then move.
/** determine the direction */
onCheckDir = (element = {}, nextElement = {}) = > {
let _dir = "", disX = element.x - nextElement.x, disY = element.y - nextElement.y;
// Determine the general direction
if (Math.abs(disX) > Math.abs(disY)) {
// Judge left and right
if (disX > 0) {
_dir = "L";
} else {
_dir = "R"; }}else {
// judge up and down
if (disY > 0) {
_dir = "T";
} else {
_dir = "B"; }}return this.curerentDir.indexOf(_dir) > -1 ? true : false;
}
Copy the code
Now that we know how to move the ball, we need to move the ball and map its path.
Mobile / * * * /
onMoveDir = () = > {
const { element, next = {}, prev = {} } = this.currentPoint;
const nextElement = next.element;
const prevElement = prev.element;
if (this.onCheckDir(element, nextElement)) {
this.ball.x = nextElement? .x;this.ball.y = nextElement? .y;this.realLine.lineStyle(8.0xf4813e.1);
this.realLine.lineTo(nextElement? .x, nextElement? .y);this.currentPoint = next;
} else if (this.onCheckDir(element, prevElement)) {
this.ball.x = prevElement? .x;this.ball.y = prevElement? .y;this.realLine.lineStyle(8.0xf4813e.1);
this.realLine.lineTo(prevElement? .x, prevElement? .y);this.currentPoint = prev; }}Copy the code
And then we’re done.
Isn’t that easy?
The moment that receives this kind of demand, the person is meng, reason slowly once, in fact wrong CP, they are not easy.
Let’s take a look at the demo:
Here the video GIF is too big, so I took the middle part to disguise it. The phone shook and the control ball traced a border to the cat.
You can see the source code here
conclusion
A word of demand, I made a drawing board, but also made a demo.πΆπΆπΆ
At the same time, the application of gyroscope in mobile terminal is investigated, so you can’t look at CP with colored glasses. After all, they can give you a lot of strange ideas.
Even if they’re love-hate ideas.
What strange need have you encountered?
Welcome to pat brick pointing, the author’s skill is still shallow, if there is improper place please be corrected.
reference
deviceorientation
The article is shallow, hope you don’t hesitate to your comments and praise ~
Note: this article is the author’s painstaking work, reprint must be declared