接到需求,需要在原生中生成带二维码的海报,于是有了这篇记录
原生中不像微信小游戏那样,有离屏画布,所以必须要采用其他的方案来做
思路如下,使用双相机实现无感截图
主相机展示 UI界面,新增截图相机来做截图
具体实现,在需要截图的界面新增一个相机,同时新增一个sceneShot的层,截图相机渲染Group为sceneShot的元素
主相机渲染剔除sceneShot层,同时设置相机深度,sceneShot优先级比主相机优先级高即可
截图相机属性
主相机属性
后续就使用 cc.RenderTexture 组件,将截图相机中的内容送进来,然后使用RenderTexture实例 调用readPixels方法完成截图,截图时需要把二维码挂在上面所以,有一个等待机制,假设code 没加载出来,那么这截图没有任何意义,因为截图时间长,所以我做了缓存,做了缓存以后,发现需要更新截图的时候不方便(比如海报变动,或者code变动),我又做了版本比对,游戏启动会去读取配置信息,根据配置信息中的shareBgVersion 与 本地存储的shareBgVersion 进行对比,若服务器版本高且图片存在,那么会删除图片重新截图,同时把服务器版本保存到本地,
以备下一次更新,代码如下
const {ccclass, property} = cc._decorator;
@ccclass
export default class UISharePanel extends UI
{
@property(cc.Node) btn_close : cc.Node = null;
@property(cc.Node) btn_friend : cc.Node = null;
@property(cc.Node) btn_circle : cc.Node = null;
@property(cc.Sprite) bg_shot : cc.Sprite = null;
@property(cc.Camera) camera : cc.Camera = null;
@property(cc.Sprite) headIcon : cc.Sprite = null;
@property(cc.Sprite) inviteCodeImage : cc.Sprite = null;
@property(cc.Node) content : cc.Node = null;
@property(cc.Node) item : cc.Node = null;
@property(cc.Node) loading : cc.Node = null;
@property(cc.PageView) pageView : cc.PageView = null;
private texture : cc.RenderTexture = null;
private prefixPath = "";
private width = 720;
private height = 1280;
private curIdx = 0; //当前索引
private shotIdx = 1;
private shotComplete : Function = null;
private picDataList = [];
private isCanShot = false; //是否可以截图
private isNeedShot = false; //是否需要截图
private pageSize = null;
private shareBgList:string[] =
[
"share_1.png",
"share_2.png",
]
public Init()
{
if(!CC_JSB)
{
return;
}
this.prefixPath = jsb.fileUtils.getWritablePath();
if(cc.sys.os == cc.sys.OS_ANDROID)
{
this.prefixPath = '/sdcard/';
}
this.pageSize = this.item.getContentSize();
this.texture = new cc.RenderTexture();
let gl = cc.game._renderContext;
this.texture.initWithSize(this.width,this.height,gl.STENCIL_INDEX8);
this.camera.targetTexture = this.texture;
this.UpdateShareInfo();
this.pageView.setCurrentPageIndex(this.curIdx);
this.curIdx ++;
}
//更新邀请信息
public UpdateShareInfo()
{
//this.inviteCodeLabel.string = "我的邀请码:"+ Player.Instance.playerInfo.invitationCode +"";
this.LoadImage(this.inviteCodeImage,Player.Instance.playerInfo.codeUrl,this.LoadFinished.bind(this));
this.LoadImage(this.headIcon,Player.Instance.playerInfo.hearImg,this.LoadFinished.bind(this));
}
private idx = 0;
public LoadFinished()
{
this.idx ++;
console.log("LoadFinished : " + this.idx);
if(this.idx>=2)
{
console.error("图片加载完毕 可以截图");
this.isCanShot = true;
if(this.isNeedShot)
{
console.error("检测要需要截图,开始截图");
this.ShotFunc();
}
}
}
public LoadImage(image:cc.Sprite, url:string,callback:Function)
{
if(!url || url == "")
{
console.log("URL 为 null");
return;
}
console.log("开始下载图片 : " + url);
cc.loader.load({url: url, type: 'png'},(err,res)=>{
if(err != null)
{
console.error("加载Image Error : " +JSON.stringify(err));
return;
}
console.log("加载图片成功 : URL : " + url);
image.spriteFrame = new cc.SpriteFrame(res);
if(callback)
{
callback();
}
});
}
// 监听事件
public OnPageEvent(sender, eventType)
{
// 翻页事件
if (eventType !== cc.PageView.EventType.PAGE_TURNING) {
return;
}
console.log("当前所在的页面索引:" + sender.getCurrentPageIndex());
this.curIdx = sender.getCurrentPageIndex() + 1;
}
public GetCurShareURL()
{
return this.prefixPath + this.shareBgList[this.curIdx-1];
}
//检查分享背景图版本
//返回值是否需要重新截图
public CheckUpdateShareBg():boolean
{
let curVersion = cc.sys.localStorage.getItem("shareBgVersion");
let serverVersion = HeroGame.Instance.GameConfigInfo.shareBgVersion;
if(!curVersion)
{
return true;
}
else
{
if(serverVersion >= curVersion)
{
return true;
}
else
{
return false;
}
}
}
public DeleteShareBg()
{
for(let i = 0;i
{
if(err)
{
console.error(err);
return;
}
else
{
console.log("加载成功");
sprite = new cc.SpriteFrame();
sprite.setTexture(res);
let node = cc.instantiate(this.item);
console.log(node.getContentSize());
node.name = "share_"+i;
this.pageView.addPage(node);
let Image = node.getComponent(cc.Sprite);
Image.spriteFrame = sprite;
node.active = true;
}
})
}
let size = this.content.getContentSize();
this.content.setContentSize(cc.size(200 + this.shareBgList.length* 534,size.height));
this.loading.active = false;
}
else
{
console.log("图片不存在,需要重新截图");
//图片不存在重新截图保存
this.shotComplete = ()=>
{
console.log("截图全部完成");
cc.sys.localStorage.setItem("shareBgVersion",HeroGame.Instance.GameConfigInfo.shareBgVersion+1); //更新背景标识
UIItem.InitList(this.content);
this.pageView.removeAllPages();
//读取展示
for(let i = 0;i
{
console.log("开始截图");
let picData = this.InitImage();
this.picDataList.push(picData);
this.SaveFile(picData,this.shotIdx); //保存
this.shotIdx++;
if(this.shotIdx
{
if(err)
{
console.error("load share bg err : " + err);
}
else
{
this.bg_shot.spriteFrame = res;
this.ShotFunc();
}
})
}
else
{
if(this.shotComplete)
{
this.shotComplete();
}
}
},0); //等一帧再次截图
}
private _width = 0;
private _height = 0;
public InitImage() : Uint8Array
{
let data = this.texture.readPixels();
this._width = this.texture.width;
this._height = this.texture.height;
let picData = this.filpYImage(data,this._width,this._height);
return picData;
}
public filpYImage (data:Uint8Array, width:number, height:number) :Uint8Array
{
let picData = new Uint8Array(width * height * 4);
let rowBytes = width * 4;
for (let row = 0; row < height; row++) {
let srow = height - 1 - row;
let start = srow * width * 4;
let reStart = row * width * 4;
// save the piexls data
for (let i = 0; i < rowBytes; i++) {
picData[reStart + i] = data[start + i];
}
}
return picData;
}
public SaveFile(picData:Uint8Array,idx:number)
{
if(CC_JSB)
{
let filePath = this.prefixPath + 'share_'+idx+'.png';
let success = jsb.saveImageData(picData, this._width, this._height, filePath)
if (success)
{
cc.log("save image data success, file: " + filePath);
}
else
{
cc.error("save image data failed!");
}
}
}
}