View的Canvas绘制黑色原因
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了View的Canvas绘制黑色原因相关的知识,希望对你有一定的参考价值。
参考技术A 最近在做一个需求,自定义view,实现一个中空的遮罩层,一开始实现的效果如下:先看实现:
乍一看,逻辑没问题,先画一个半透明的遮罩,然后通过PorterDuff.Mode.CLEAR,在中间挖空。
由于之前分析过 view如何显示到手机屏幕 ,所以知道canvas是从viewrootimpl新建分给各个view的
ViewRootImpl->drawSoftware:
Surface->lockCanvas:
从上面可以知道,mCanvas是设备窗口的一个句柄.
也是就是说,如果你直接在canvas上面绘制数据相当于在window上面直接绘制,所有效果将对应到当前的window上面,也就是会出现上面的中间黑色的原因,window的背景为黑色.
但是由于canvas相当于ps中图层的意思,遵循PS的绘制原则,是不允许在原图层绘制数据的,所以数据的绘制必须要新建图层绘制,在这里,也要遵循这个原则,所有在draw(canvas)上面绘制数据,必须新建图层
新建图层:
canvas提供的savelayer方法,新建了一个新的图层
还有另外一种类似新建图层的方法,就是创建一个新的bitmap对象,所有绘制操作的都作用于当前的bitmap,
然后将最后的bitmap绘制到view的canvas上面;
完.
Canvas-橡皮擦在画布保存为图像后在画布上绘制黑色线条
在画布上绘图工作得很好。即使橡皮擦也工作得很好。问题是,当画布保存为图像时,它绘制的是黑色线而不是橡皮擦。
为了更好地理解,我添加了屏幕截图和代码。
1.在删除抽奖时 -
一个。源代码 -
erase(){
this.ctx.globalCompositeOperation = 'destination-out';
}
handleMove(ev){
// let ctx = this.canvasElement.getContext('2d');
let currentX = ev.touches[0].pageX - this.offsetX;
let currentY = ev.touches[0].pageY - this.offsetY;
this.ctx.beginPath();
this.ctx.lineJoin = "round";
this.ctx.moveTo(this.lastX, this.lastY);
this.ctx.lineTo(currentX, currentY);
this.ctx.closePath();
this.ctx.strokeStyle = this.currentColour;
this.ctx.lineWidth = this.brushSize;
this.ctx.stroke();
this.undoList.push({
x_start: currentX,
y_start: currentY,
x_end: this.lastX,
y_end: this.lastY,
color: this.currentColour,
size: this.brushSize,
mode: this.ctx.globalCompositeOperation
});
this.lastX = currentX;
this.lastY = currentY;
}
湾输出 -
2.画布保存为图像 -
一个。代码 -
this.ctx.clearRect(0, 0, this.canvasElement.width, this.canvasElement.height);
setTimeout(() => {
// this.drawImg(this.newImg);
for(let i=0; i<this.textAreasList.length; i++){
let txt = this.textAreasList[i];
this.ctx.font = this.textAreasList[i].bold + ' ' + this.textAreasList[i].italic + ' ' + this.textAreasList[i].fontSize + ' ' + 'Comic Sans MS';
this.ctx.fillStyle = this.textAreasList[i].color;
if(this.textAreasList[i].left=="" || this.textAreasList[i].left==undefined) {
this.textAreasList[i].left = 50;
}
if(this.textAreasList[i].top=="" || this.textAreasList[i].top==undefined) {
this.textAreasList[i].top = 50;
}
this.ctx.fillText(this.textAreasList[i].value, this.textAreasList[i].left, this.textAreasList[i].top);
}
this.redrawCanvas(this.undoUseList);
let imgPath = this.canvasElement.toDataURL('image/png');
let message= "";
this.base64ToGallery.base64ToGallery(imgPath).then(
res => message = "Image saved to gallery!",
err => message = "Something went wrong!!"
);
this.spinner.hide();
let toast = this.toastCtrl.create({
message: message,
duration: 3000,
position: 'bottom',
cssClass: 'changeToast'
});
this.navCtrl.push(HomePage);
}, 5000);
}
redrawCanvas(arr){
// this.ctx.globalCompositeOperation = 'source-over';
for(let i=0; i<arr.length; i++){
for(let j=0; j< arr[i].length; j++){
let ctx = this.canvasElement.getContext('2d');
ctx.globalCompositeOperation = arr[i][j].mode;
console.log('x start', arr[i][j].x_start);
console.log('y start', arr[i][j].y_start);
console.log('x end', arr[i][j].x_end);
console.log('y end', arr[i][j].y_end);
ctx.beginPath();
ctx.lineJoin = "round";
ctx.moveTo(arr[i][j].x_start, arr[i][j].y_start);
ctx.lineTo(arr[i][j].x_end, arr[i][j].y_end);
ctx.closePath();
ctx.strokeStyle = arr[i][j].color;
ctx.lineWidth = arr[i][j].size;
ctx.stroke();
}
}
}
**b. Output -**
我不明白如何将画布保存为黑色,同时将画布保存为图像。
答案
所以,你正在使用撤消列表重绘画布,对吧?然后使用toDataUrl()输出图像?
在我看来,问题在于此
this.undoList.push({
x_start: currentX,
y_start: currentY,
x_end: this.lastX,
y_end: this.lastY,
color: this.currentColour, <== Is this an object?
size: this.brushSize,
mode: this.ctx.globalCompositeOperation
});
如果this.currentColour是一个对象,我想在代码中的其他地方更改了属性,当你重新构建步骤时,你会得到黑色的样式,不确定它是否是默认样式。
所以你可以试试这个
this.undoList.push({
x_start: currentX,
y_start: currentY,
x_end: this.lastX,
y_end: this.lastY,
color: {
prop1: this.currentColour.prop1
prop2: this.currentColour.prop2
...
}
size: this.brushSize,
mode: this.ctx.globalCompositeOperation
});
用你在该对象中的实际属性替换prop1,prop2等。通过这种方式,您将创建一个新的obj(复制它),而不是将ref传递给旧的。
你可以让它变得更加漂亮,但是像这样你可以更好地得到推理。
另一答案
最后我解决了这个问题。我做了以下改动。
redrawCanvas(arr){
// this.ctx.globalCompositeOperation = 'source-over';
for(let i=0; i<arr.length; i++){
for(let j=0; j< arr[i].length; j++){
let ctx = this.canvasElement.getContext('2d');
// ctx.globalCompositeOperation = arr[i][j].mode;
if(arr[i][j].mode== "destination-out"){
// ctx.globalCompositeOperation = "destionation-out";
// ctx.strokeStyle = "rgba(0,0,0,0.2)";
let cImg = new Image();
cImg.src = this.selectedImage;
let pattern = ctx.createPattern(cImg, "no-repeat");
ctx.strokeStyle = pattern;
}else{
ctx.strokeStyle = arr[i][j].color;
}
console.log('x start', arr[i][j].x_start);
console.log('y start', arr[i][j].y_start);
console.log('x end', arr[i][j].x_end);
console.log('y end', arr[i][j].y_end);
ctx.beginPath();
ctx.lineJoin = "round";
ctx.moveTo(arr[i][j].x_start, arr[i][j].y_start);
ctx.lineTo(arr[i][j].x_end, arr[i][j].y_end);
ctx.closePath();
ctx.lineWidth = arr[i][j].size;
ctx.stroke();
}
}
}
如您所见,我添加了一个检查以检查它是刷子还是橡皮擦。对于橡皮擦,我正在检查它是否是destination-out
。
如果它是橡皮擦,我正在创造新的形象,以制作strokestyle
。
所以你可以说我只做了以下更改。
if(arr[i][j].mode== "destination-out"){
// ctx.globalCompositeOperation = "destionation-out";
// ctx.strokeStyle = "rgba(0,0,0,0.2)";
let cImg = new Image();
cImg.src = this.selectedImage;
let pattern = ctx.createPattern(cImg, "no-repeat");
ctx.strokeStyle = pattern;
}else{
ctx.strokeStyle = arr[i][j].color;
}
以上是关于View的Canvas绘制黑色原因的主要内容,如果未能解决你的问题,请参考以下文章
android canvas view重绘 我在一个activity中调用了canvas。在canvas中我点击事件需要重新绘制当前view