ScrollView + RichText实现可滑动的富文本显示自定义组件
通过将ScrollView滑动视图组件和RichText富文本组件组合,同时实现文字自动滑动的效果。
为适应和实现多种不同的显示效果,我设计组件的节点树为
ContentDisplay
├──Background
├──Foreground
│ ├──TextContent
ContentDisPlay节点作为整个组件的根节点,并挂载脚本实现组件的功能。
Background节点作为整个组件的背景节点,可为富文本显示添加背景,例如背景图,或做成遮罩层。
Foreground表示整个富文本显示的区域。其中TextContent同时挂载了RichText和ScrollView组件。
上图中蓝色框为Background,灰色框为Foreground。
接下来是重点,通过脚本控制RichText的滑动。我将脚本分为三个文件,分别挂载到ContentDisplay根节点和Background,Foreground节点上。
此处主要实现了前景的富文本滑动,实际上Background未包含脚本。计划后面再加上对背景的个性控制。这里只写了ContentDisplay和Foreground脚本。
ContentDisplay 脚本,两个子节点作为成员变量。在其他地方通过此对象的此成员变量引用和调用。
//ContentDisplay 脚本,两个子节点作为成员变量。在其他地方通过此对象的此成员变量引用和调用。
//import
import foreground from "./foreground"
import background from "./background"
const {ccclass, property} = cc._decorator;
/**
* contextDisplay is a custom component to show some rich text.
* It's include a background view layer and foreground view layer.
* the background layer to do some thing like mask work
* the foreground layer is for show rich text
*/
@ccclass
export default class contentDisplay extends cc.Component {
public background: background;
public foreground: foreground;
onLoad () {
console.log("----onLoad contentDisplay----");
this.background = this.node.getChildByName("Background").getComponent(background);
this.foreground = this.node.getChildByName("Foreground").getComponent(foreground);
}
}
Foreground脚本,提供对文本内容,区域大小,是否自动滑动,是否自动返回顶部的设置接口。
//Foreground脚本,提供对文本内容,区域大小,是否自动滑动,是否自动返回顶部的设置接口。
const {ccclass, property} = cc._decorator;
@ccclass
export default class foreground extends cc.Component {
//子节点,该节点挂载了富文本和滑动组件
private textContentNode: cc.Node;
//富文本组件
private richText: cc.RichText;
//滑动组件
private scroll: cc.ScrollView;
//当前状态是否可以自动滑动。
private canSlider: boolean = false;
//属性,设置当滑动到底部之后是否自动返回顶部
@property(Boolean)
public autoToTop: boolean = true;
//属性,设置是否自动滑动
@property(Boolean)
private autoSlider: boolean = false;
//属性,设置滑动的时间间隔。
@property(Number)
private sliderInterval: number = 0.01
//属性,设置滑动每次时间间隔滑动的距离。
@property(Number)
private sliderSpeed: number = 50;
onLoad () {
console.log("----onLoad foreground----");
this.textContentNode = this.node.getChildByName("TextContent");
this.richText = this.textContentNode.getComponent(cc.RichText);
this.scroll = this.textContentNode.getComponent(cc.ScrollView);
}
start(){
//初始化后,判断属性,是否需要自动滑动
if(this.autoSlider){
this.addAutoSlider();
}
//由于官方自带组件ScrollView的问题,当取消滚动惯性之后,scroll-to-bottom事件不会被触发,此处需要自己监听是否滑动到底部。并判断是否需要返回顶部。
this.scroll.node.on("scroll-to-bottom", function ( event ) {
console.log(this.autoToTop);
if(this.autoToTop){
this.scroll.content.setPosition(0, 0);
}
}.bind(this));
}
//设置文本区的宽高
setTextContentNode(height: number, width: number){
this.textContentNode.height = height;
this.textContentNode.width = width;
this.richText.maxWidth = width;
}
//设置富文本内容
setContent(string: string){
this.richText.string = string;
}
//设置富文本显示位置
setContentPosition(position: cc.Vec2){
this.scroll.content.setPosition(position);
}
//获取富文本当前显示的位置
getContentPosition(): cc.Vec2{
return this.scroll.content.getPosition();
}
//获取富文本内容
getContent(): string{
return this.richText.string;
}
//通过计时器设置自动滑动
addAutoSlider(){
this.autoSlider = true;
this.scroll.content.setPosition(0, 0);
if(this.autoSlider){
this.schedule(this.autoSliderCallback.bind(this),this.sliderInterval);
}
}
//自动滑动计时器的callback
autoSliderCallback(){
//Judging whether or not to slider
if(this.scroll.content.getPosition().y >= this.scroll.content.height - this.node.height * 0.5){
this.canSlider = false;
this.scroll.node.emit("scroll-to-bottom");
}else{
this.canSlider = true;
}
if(!this.canSlider){
return;
}
let x: number = this.scroll.content.getPosition().x;
let y:number = this.scroll.content.getPosition().y + this.sliderSpeed * this.sliderInterval;
this.scroll.content.setPosition(x, y);
}
//取消自动滑动
cancelAutoSlider(){
this.unschedule(this.autoSliderCallback);
this.autoSlider = false;
}
}
测试脚本
import contentDisplay from "../contentDisplay/contentDisplay"
const {ccclass, property} = cc._decorator;
@ccclass
export default class gameTest extends cc.Component {
cd: contentDisplay = null;
endPoem: string = "";
onLoad () {
console.log("----onLoad contentDisplay----");
this.cd = cc.find("Canvas/ContentDisplay").getComponent(contentDisplay);
console.log(this.cd);
}
start () {
this.cd.foreground.setTextContentNode(480, 640);
cc.loader.loadRes('data/endPoem.json', function(err,res){
if (err) {
cc.log(err);
}else{
let list=res;
this.cd.foreground.setContent(list.json.endPoem);
this.cd.foreground.addAutoSlider();
}
}.bind(this));
}
}
此处使用了Minecraft的<< End Poem >>来做测试。
最终显示效果
完成组建之后,拖拽到资源目录成为预制体,就可以到处使用了。
这是第一次尝试制作cocos cretor自定义组件,还有很多不足的地方,欢迎大家批评指点。