Laya图片编辑,保存,读取

游戏项目中有个一个需要用户上传,编辑图片的需求,特此记录方案。

遇到的问题

在整个实现功能的过程中,主要遇到的重大功能有:

  • 用户上传图片并显示
  • 编辑图片且保存编辑之后的图片内容
  • 读取此前编辑好的存在本地的内容

上传用户的图片

Laya没有本地文件的交互功能,所以使用了H5原生的file操作,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
	let worldPos =  this._pageMyUpload.localToGlobal(new Laya.Point(_x,_y));
var gamesBox = document.getElementById("floatLayer");
this._imageLoader = document.createElement("input");
this._imageLoader.type = 'file';
this._imageLoader.style.width = "280px";
this._imageLoader.style.height = "24px";
this._imageLoader.style.left =Math.floor(worldPos.x / Laya.Browser.pixelRatio).toString() + "px";
this._imageLoader.style.top =Math.floor(worldPos.y / Laya.Browser.pixelRatio).toString() + "px";
this._imageLoader.style.zIndex = "1000";
this._imageLoader.style.opacity = 0;
this._imageLoader.style.position = "absolute";
this._imageLoader.accept = "image/jpg,image/jpeg,image/png";
this._imageLoader.id = "imageLoader";
Laya.Browser.document.body.appendChild(this._imageLoader);

var fileReader: any = new Laya.Browser.window.FileReader();
let self = this;
this._imageLoader.onchange = function (e: any): void {
if (self._imageLoader.files.length > 0) {
fileReader.readAsDataURL(self._imageLoader.files[0]);
}
};

fileReader.onload = function (evt): void {
if (Laya.Browser.window.FileReader.DONE == fileReader.readyState) {
let bufferData = this.result;
self.onLoadImage(bufferData);
}
};

在完成load之后,通过fileReader.readAsDataURL方法读取成texure并显示出来。

1
2
3
4
5
6
7
8
9
10
11
private onLoadImage(data:any):void
{
var sp:Sprite = new Sprite();
sp.loadImage(data,0,0,100,100,Laya.Handler.create(this, this.onLoadImageOver));
}

private onLoadImageOver(e:any):void
{
this._imageEditorUser.curTexture = e;
this._imageEditorUser.isEditing = true;
}

编辑并保存

编辑方面其实很简单,就是将加载的texture传给一个Sprite之后,所有的编辑都基于这个Sprite进行。我们要做的是在缩放之后计算需要裁减的位置和大小,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public SaveCurImg():void
{
if(this._mainSp && this._mainSp.texture)
{
let texture:Laya.Texture = new Laya.Texture();
let curCornorPosX = this._mainSp.x - this._mainSp.width * .5 * this._mainSp.scaleX;
let curCornorPosY = this._mainSp.y - this._mainSp.height * .5 * this._mainSp.scaleY;
this._mainSp.pivotX = 0;
this._mainSp.pivotY = 0;
this._mainSp.x = curCornorPosX;
this._mainSp.y = curCornorPosY;
let startX = (40 - this._mainSp.x ) / this._mainSp.scaleX ;
let startY = (24 - this._mainSp.y) / this._mainSp.scaleY;
texture = Laya.Texture.create(this._mainSp.texture,startX,startY,this._cardWdith / this._mainSp.scaleX ,this._cardHeight / this._mainSp.scaleY ,0,0);
texture.width = this._cardWdith;
texture.height = this._cardHeight;

this._mainSp.texture = null;

this._editPanel.visible = false;
this._editable = false;
this._isEditing = false;
this._mainSp.scale(1,1);
this._mainSp.texture = texture;
this._mainSp.x = 40;
this._mainSp.y = 24;

this.event("finishEdit",texture);
}
}

为了让缩放显得正常,我们在给Sprite进行texture赋值之后,会将Sprite的缩放中心放在图片的中心,然而在通过Laya.Texture.create方法进行生成新的texture

的时候,需要进行一定的换算,来确定在当前缩放倍数的情况下准确地对当前的纹理进行截取。

1
2
3
4
5
6
7
8
//起点位置计算
let curCornorPosX = this._mainSp.x - this._mainSp.width * .5 * this._mainSp.scaleX;
let curCornorPosY = this._mainSp.y - this._mainSp.height * .5 * this._mainSp.scaleY;
...
//截取尺寸计算
let startX = (40 - this._mainSp.x ) / this._mainSp.scaleX ;
let startY = (24 - this._mainSp.y) / this._mainSp.scaleY;
texture = Laya.Texture.create(this._mainSp.texture,startX,startY,this._cardWdith / this._mainSp.scaleX ,this._cardHeight / this._mainSp.scaleY ,0,0);

而这个texture就是我们要存到数据库的内容。

但是值得注意的是,所有对于texture截取保存的操作,其源纹理的bitmap是不会变化的,也就是说,对这张texture的操作知识改变了这张texture的uv属性,而这个uv属性在重新加载这个texture之后会变成默认值,这就导致无论我们之前对源图片进行了什么操作,等我们重新加载操作之后的图片,其纹理还是源头问题的样子,所以我们要保存这个uv内容。

IndexedDB

首先对于图片的读取和保存用到了IndexedDB,这是一个H5的本地数据库。

保存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public static saveTextureData(_name:string,_dirName:string,_data:Laya.Texture):void
{
let self = this;
let request = window.indexedDB.open('textureDataBase',3);
request.onerror = function(event) {
console.error("打开图片数据库失败");
};
request.onupgradeneeded = function(event:any) {
// 保存 IDBDataBase 接口
var db = event.target.result;

// 为该数据库创建一个对象仓库
if(!db.objectStoreNames.contains(_dirName))
{
var objectStore = db.createObjectStore(_dirName, { keyPath: "name" });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('data', 'data', { unique: false });
objectStore.createIndex('uv', 'uv', { unique: false });
}

};
request.onsuccess = function(event:any) {
console.log("打开图片数据库成功");
let db = event.target.result;
var trans = db.transaction([_dirName], "readwrite");
var objectStore:IDBObjectStore = trans.objectStore(_dirName);
let url = _data.bitmap.url;
if(objectStore.get(_name))
{
objectStore.put({name:_name,data:_data.bitmap.url,uv:_data.uv});
}
else
{
objectStore.add({name:_name,data:_data.bitmap.url,uv:_data.uv});
}

trans.onerror = function(event) {
console.error("保存图片数据库失败");
};
trans.onsuccess = function(event) {
TipUtils.ShowTip("图片保存成功");
};

};
}
读取

遍历当前数据库且抛出数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public static GetUserUploadPics():void
{
let self = this;
let request = window.indexedDB.open('textureDataBase',3);
request.onerror = function(event) {
console.error("打开图片数据库失败");
};
request.onupgradeneeded = function(event:any) {
// 保存 IDBDataBase 接口
var db = event.target.result;
// 为该数据库创建一个对象仓库
if(!db.objectStoreNames.contains("userCard_Img"))
{
var objectStore = db.createObjectStore("userCard_Img", { keyPath: "name" });
objectStore.createIndex('name', 'name', { unique: false });
objectStore.createIndex('data', 'data', { unique: false });
objectStore.createIndex('uv', 'uv', { unique: false });
}
};
request.onsuccess = function(event:any) {
console.log("打开图片数据库成功");
let db = event.target.result;
var trans = db.transaction(["userCard_Img"], "readonly");
var objectStore:IDBObjectStore = trans.objectStore("userCard_Img");
// objectStore.getAll();
objectStore.openCursor().onsuccess = function(_event:any){
var cursor = _event.target.result;
if(cursor)
{
console.log("read ---> ",cursor.value.data);
EventDispatcher.GetInstance().event("LoadPicOver",[cursor.value.data,cursor.value.uv]);
cursor.continue();
}
else
{
console.log("没有更多数据了");
}
}
trans.onerror = function(event) {
console.error("读取数据库失败");
};
trans.onsuccess = function(event) {
TipUtils.ShowTip("读取图片成功");
};

};
}