123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- <template>
- <div class="paint">
- <canvas ref="canvas" v-show="show"></canvas>
- <!-- <div class="toolbar" v-show="showPaint">
- <ul>
- <li @click="onDraw('drawStart')" v-show="show == false">
- <i class="iconfont icontagging"></i>
- <div>画笔</div>
- </li>
- <li @click="onDraw('drawUndo')" v-if="show" :class="{ disable: !canUndo }">
- <i class="iconfont icon_cancel"></i>
- <div>撤回</div>
- </li>
- <li @click="onDraw('drawStop')" v-if="show">
- <i class="iconfont iconclose"></i>
- <div>关闭</div>
- </li>
- </ul>
- </div> -->
- </div>
- </template>
- <script lang="ts">
- import { getRole, sendToH5 } from "../../../utils/rtc_socket";
- import { objects } from "/@/core/base";
- import math from "/@/core/util/math";
- import convertTool from "/@/core/util/convertTool";
- export default {
- props: {
- showPaint: Boolean,
- },
- data() {
- return {
- role: getRole(),
- show: false,
- canUndo: false,
- colorA: "#02c8ae",
- colorB: "#2e98fe",
- };
- },
- watch: {
- showPaint() {
- if (this.showPaint) {
- this.role = getRole();
- }
- },
- show() {
- this.$bus.emit("shop/header/disable", this.show);
- },
- },
- created() {
- this.$bus.on("shop/sync/action", (data) => {
- if (data.type == "drawStart") {
- this.show = true;
- this.draw = [];
- this.drawHistory = [];
- this.$nextTick(() => {
- this.onDrawStart();
- });
- } else if (data.type == "drawStop") {
- this.show = false;
- this.draw = null;
- this.drawHistory = null;
- } else if (data.type == "drawing") {
- const draw = this.transformTo2d(data.data.drawing);
- if (data.data.role != this.role) {
- this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
- this.drawing(draw);
- }
- } else if (data.type == "drawUndo") {
- this.drawUndo(data.data.role);
- }
- });
- },
- mounted() {
- this.canvas = this.$refs.canvas;
- this.context = this.canvas.getContext("2d");
- this.canvas.onmousedown = (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- this.beginStroke({
- x: e.clientX,
- y: e.clientY,
- });
- };
- this.canvas.onmouseup = (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- this.endStroke();
- };
- this.canvas.onmouseout = (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- this.endStroke();
- };
- this.canvas.onmousemove = (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- if (this._mouseDown) {
- this.moveStroke({
- x: e.clientX,
- y: e.clientY,
- });
- }
- };
- let touch;
- // 移动端触控
- this.canvas.addEventListener("touchstart", (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- touch = e.touches[0];
- this.beginStroke({
- x: touch.pageX,
- y: touch.pageY,
- });
- });
- this.canvas.addEventListener("touchmove", (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- if (this._mouseDown) {
- touch = e.touches[0];
- this.moveStroke({
- x: touch.pageX,
- y: touch.pageY,
- });
- }
- });
- this.canvas.addEventListener("touchend", (e) => {
- if (!this.show || this.role != "leader") {
- return;
- }
- e.preventDefault();
- this.endStroke();
- });
- this.mouse = new THREE.Vector2();
- },
- methods: {
- /**
- * 2d数据转3d数据
- */
- transformTo3d(draw) {
- const data = [];
- if (draw.length == 0) {
- return [];
- }
- draw.forEach((item, index) => {
- math.convertScreenPositionToNDC(item.x, item.y, this.mouse);
- var intersect = .getMouseIntersect(
- objects.player.camera,
- [this.intersectPlane],
- this.mouse
- );
- if (!intersect) {
- console.error("no intersect ??");
- } else {
- item.pos3d = intersect.point;
- data.push(item);
- }
- });
- return data;
- },
- /**
- * 3d数据转2d数据
- */
- transformTo2d(draw) {
- const data = [];
- draw.forEach((item) => {
- var pos3d = new THREE.Vector3(item.pos3d.x, item.pos3d.y, item.pos3d.z);
- var pos2d = convertTool.getPos2d(pos3d, objects.player.camera);
- //delete item.pos3d;
- item.x = pos2d.pos.x;
- item.y = pos2d.pos.y;
- data.push(item);
- });
- return data;
- },
- onDraw(type) {
- if (type == "drawStart") {
- this.show = true;
- this.draw = [];
- this.drawHistory = [];
- this.$nextTick(() => {
- this.onDrawStart();
- });
- } else if (type == "drawStop") {
- this.show = false;
- this.draw = null;
- this.drawHistory = null;
- } else if (type == "drawing") {
- const draw = this.transformTo2d(data.content.drawing);
- if (data.role != role) {
- this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
- //this.drawing(draw);
- }
- } else if (type == "drawUndo") {
- this.drawUndo(this.role);
- }
- sendToH5({
- type,
- data: {
- role: this.role,
- },
- });
- },
- onPainting() {
- const draw = this.transformTo3d(this.draw);
- this.drawHistory.push(JSON.parse(JSON.stringify(draw)));
- sendToH5({
- type: "drawing",
- data: {
- drawing: draw,
- },
- });
- this.draw = [];
- this.canUndo = true;
- this._endTime = 0;
- this._mouseDown = false;
- this._lastTimestamp = 0;
- this.$emit("sendCanUndo", this.canUndo);
- },
- beginStroke(point) {
- this._mouseDown = true;
- this._lastTimestamp = Date.now();
- this._lastPosition = this.windowToCanvas(point.x, point.y);
- this.draw.push({
- role: this.role,
- width: 0,
- x: this._lastPosition.x,
- y: this._lastPosition.y,
- t: 5, //this._lastTimestamp - this._endTime
- });
- },
- onDrawStart() {
- let dpr = window.devicePixelRatio || 1;
- let rect = this.canvas.getBoundingClientRect();
- this.ratio = 1; // window.innerWidth / 375;
- this.canvas.width = rect.width * dpr;
- this.canvas.height = rect.height * dpr;
- this.context.scale(dpr, dpr);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this._endTime = 0;
- this._mouseDown = false;
- this._lastTimestamp = 0;
- this._lastLineWidth = -1;
- this._lastPosition = {
- x: 0,
- y: 0,
- };
- math.convertScreenPositionToNDC(0, 0, this.mouse); //取屏幕中点
- var intersect = convertTool.getMouseIntersect(
- objects.player.camera,
- [objects.model.skybox, objects.sceneRenderer.scene.skyboxBG],
- this.mouse
- );
- this.placeIntersectPlane(intersect && intersect.point);
- },
- moveStroke(point) {
- let timestamp = Date.now();
- let position = this.windowToCanvas(point.x, point.y);
- let s = this.calcDistance(position, this._lastPosition);
- let t = timestamp - this._lastTimestamp;
- let lineWidth = this.calcLineWidth(t, s);
- //draw
- this.context.beginPath();
- this.context.moveTo(this._lastPosition.x, this._lastPosition.y);
- this.context.lineTo(position.x, position.y);
- this.draw.push({
- role: this.role,
- width: lineWidth,
- x: position.x,
- y: position.y,
- t: 5, //t
- });
- this.context.strokeStyle = this.colorA;
- this.context.lineWidth = lineWidth;
- this.context.lineCap = "round";
- this.context.linJoin = "round";
- this.context.stroke();
- //每次过程结束时,将结束值赋给初始值,一直延续
- this._lastPosition = position;
- this._lastTimestamp = timestamp;
- this._lastLineWidth = lineWidth;
- },
- endStroke() {
- this.draw.push({
- role: this.role,
- width: 0,
- x: this._lastPosition.x,
- y: this._lastPosition.y,
- t: 0,
- });
- this.onPainting();
- this._mouseDown = false;
- this._endTime = Date.now();
- },
- calcLineWidth(t, s) {
- let v = s / t;
- let resultLineWidth;
- if (v <= 0.1) {
- resultLineWidth = 6;
- } else if (v >= 3) {
- resultLineWidth = 2;
- } else {
- resultLineWidth = 6 - ((v - 0.1) / (3 - 0.1)) * (6 - 4);
- }
- if (this._lastLineWidth == -1) {
- return resultLineWidth;
- }
- return (this._lastLineWidth * 2) / 3 + (resultLineWidth * 1) / 3;
- },
- calcDistance(pos1, pos2) {
- return Math.sqrt(
- (pos1.x - pos2.x) * (pos1.x - pos2.x) +
- (pos1.y - pos2.y) * (pos1.y - pos2.y)
- ); //通过起始结束坐标x,y值计算路程长度
- },
- windowToCanvas(x, y) {
- var bbox = this.canvas.getBoundingClientRect(); //获取canvas的位置信息
- return {
- x: Math.round(x - bbox.left),
- y: Math.round(y - bbox.top),
- }; //返回当前鼠标相对于canvas的位置
- },
- drawing(draw) {
- for (let i = 0; i < draw.length - 1; i++) {
- draw[i].t &&
- setTimeout(() => {
- this.context.beginPath();
- this.context.strokeStyle =
- draw[i].role == this.role ? this.colorA : this.colorB;
- this.context.moveTo(draw[i].x * this.ratio, draw[i].y * this.ratio);
- this.context.lineTo(
- draw[i + 1].x * this.ratio,
- draw[i + 1].y * this.ratio
- );
- this.context.lineWidth = draw[i].width * this.ratio;
- this.context.lineCap = "round";
- this.context.linJoin = "round";
- this.context.stroke();
- }, 5);
- }
- },
- drawUndo(sender) {
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- for (let i = this.drawHistory.length - 1; i >= 0; i--) {
- if (this.drawHistory[i][0].role == sender) {
- this.drawHistory.splice(i, 1);
- break;
- }
- }
- this.drawHistory.forEach((draw) => {
- for (let i = 0; i < draw.length - 1; i++) {
- if (draw[i].t) {
- this.context.beginPath();
- this.context.strokeStyle =
- draw[i].role == this.role ? this.colorA : this.colorB;
- this.context.moveTo(draw[i].x * this.ratio, draw[i].y * this.ratio);
- this.context.lineTo(
- draw[i + 1].x * this.ratio,
- draw[i + 1].y * this.ratio
- );
- this.context.lineWidth = draw[i].width * this.ratio;
- this.context.lineCap = "round";
- this.context.linJoin = "round";
- this.context.stroke();
- }
- }
- });
- this.canUndo = this.drawHistory.some((item) => item[0].role == this.role);
- },
- placeIntersectPlane(pos) {
- //用于判断mesh拖拽移动距离的平面 需要和视线垂直,以保证遮住视野范围
- if (!this.intersectPlane) {
- var geo = new THREE.PlaneGeometry(8000, 80000, 1, 1);
- //var geo = new THREE.PlaneGeometry(3,3,1,1);
- this.intersectPlane = new THREE.Mesh(
- geo,
- new THREE.MeshBasicMaterial({
- transparent: true,
- wireframe: false,
- opacity: 0,
- side: THREE.DoubleSide,
- depthTest: false,
- })
- );
- this.intersectPlane.lookAt(new THREE.Vector3(0, 1, 0));
- this.intersectPlane.name = "intersectPlane";
- objects.model.add(this.intersectPlane);
- }
- if (pos) {
- this.intersectPlane.position.copy(pos);
- var cameraDir = objects.player.getDirection(
- null,
- objects.player.camera
- ); //向里
- this.intersectPlane.lookAt(pos.clone().add(cameraDir)); //看向相机
- }
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .paint {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- z-index: 2000;
- pointer-events: none !important;
- canvas {
- width: 100vw;
- height: 100vh;
- image-rendering: pixelated;
- image-rendering: crisp-edges;
- pointer-events: auto;
- position: fixed;
- top: 0;
- left: 0;
- }
- .toolbar {
- pointer-events: auto;
- position: absolute;
- right: 0.35rem;
- bottom: 4.5rem;
- padding: 0.4rem 0.2rem;
- border-radius: 30px;
- z-index: 100;
- background-color: rgba(0, 0, 0, 0.3);
- ul,
- li {
- margin: 0;
- padding: 0;
- list-style: none;
- }
- li {
- padding: 0.3px;
- text-align: center;
- font-size: 14px;
- margin-bottom: 0.5rem;
- &:last-child {
- margin-bottom: 0;
- }
- i {
- font-size: 20px;
- &.icon_cancel {
- font-size: 22px;
- }
- &.iconclose {
- font-size: 14px;
- }
- }
- }
- }
- }
- </style>
|