Web前端图形滑块检验组件实现
组件渲染图形:
初始化: 滑到时:
验证成功: 验证失败:
适用场景:
相信大家看了此组件图片后已经明白它的使用场景,在此不做过多介绍
组件内容:
组件CSS(sliderImgPuzzle.css):
- .block {
- position: absolute;
- left:;
- top: 0
- }
- .canvasContainer {
- /*display: none;*/
- position: absolute;
- bottom: 65px;
- background: #fff
- }
- .sliderImgPuzzleContainer {
- position: relative;
- text-align: center;
- width: 325px;
- height: 50px;
- font-size: 14px;
- line-height: 50px;
- background: #f7f9fa;
- color: #45494c;
- border: 1px solid #e4e7eb
- }
- .sliderImgPuzzleContainer_active .sliderImgPuzzle {
- height: 48px;
- top: -1px;
- border: 1px solid #1991fa
- }
- .sliderImgPuzzleContainer_active .sliderImgPuzzleMask {
- height: 48px;
- border-width: 1px
- }
- .sliderImgPuzzleContainer_success .sliderImgPuzzle {
- height: 48px;
- top: -1px;
- border: 1px solid #52ccba;
- background-color: #52ccba !important
- }
- .sliderImgPuzzleContainer_success .sliderImgPuzzleMask {
- height: 48px;
- border: 1px solid #52ccba;
- background-color: #d2f4ef
- }
- .sliderImgPuzzleContainer_success .sliderImgPuzzleIcon {
- background-position: 0 0 !important
- }
- .sliderImgPuzzleContainer_fail .sliderImgPuzzle {
- height: 48px;
- top: -1px;
- border: 1px solid #f57a7a;
- background-color: #f57a7a !important
- }
- .sliderImgPuzzleContainer_fail .sliderImgPuzzleMask {
- height: 48px;
- border: 1px solid #f57a7a;
- background-color: #fce1e1
- }
- .sliderImgPuzzleContainer_fail .sliderImgPuzzleIcon {
- top: 19px;
- background-position: 0 -82px !important
- }
- .sliderImgPuzzleContainer_active .sliderImgPuzzleText, .sliderImgPuzzleContainer_success .sliderImgPuzzleText, .sliderImgPuzzleContainer_fail .sliderImgPuzzleText {
- visibility: hidden
- }
- .sliderImgPuzzleMask {
- position: absolute;
- left:;
- top:;
- height: 50px;
- border: 0 solid #1991fa;
- background: #d1e9fe
- }
- .sliderImgPuzzle {
- position: absolute;
- top:;
- left:;
- width: 50px;
- height: 48px;
- background: #fff;
- box-shadow: 0 0 3px rgba(0, 0, 0, .3);
- cursor: pointer;
- transition: background .2s linear
- }
- .sliderImgPuzzle:hover {
- background: #1991fa
- }
- .sliderImgPuzzle:hover .sliderImgPuzzleIcon {
- background-position: 0 -13px
- }
- .sliderImgPuzzleIcon {
- position: absolute;
- top: 19px;
- left: 17px;
- width: 14px;
- height: 12px;
- background: url(../images/icon.png) 0 -26px;
- background-size: 34px 471px
- }
- .refreshIcon {
- position: absolute;
- right:;
- top:;
- width: 34px;
- height: 34px;
- cursor: pointer;
- background: url(../images/icon.png) 0 -437px;
- background-size: 34px 471px
- }
组件js(sliderImgPuzzle.js)
- ;(function ($) {
- $.fn.SliderImgPuzzle = function (setting) {
- var defaults = {callback: false}
- var setting = $.extend(defaults, setting);
- const l = 42, r = 10, w = 325, h = 155, PI = Math.PI
- const L = l + r * 2
- function getRandomNumberByRange(start, end) {
- return Math.round(Math.random() * (end - start) + start)
- }
- function createCanvas(width, height) {
- const canvas = createElement('canvas')
- canvas.width = width
- canvas.height = height
- return canvas
- }
- function createImg(onload) {
- const img = createElement('img')
- img.crossOrigin = "Anonymous"
- img.onload = onload
- img.onerror = () => {
- img.src = getRandomImg()
- }
- img.src = getRandomImg()
- return img
- }
- function createElement(tagName) {
- return document.createElement(tagName)
- }
- function addClass(tag, className) {
- tag.classList.add(className)
- }
- function removeClass(tag, className) {
- tag.classList.remove(className)
- }
- function getRandomImg() {
- return 'https://picsum.photos/300/150/?image=' + getRandomNumberByRange(0, 200)
- }
- function draw(ctx, operation, x, y, type) {
- if (type == 1)
- draw1(ctx, operation, x, y); else if (type == 2)
- draw2(ctx, operation, x, y); else if (type == 3)
- draw3(ctx, operation, x, y); else if (type == 4)
- draw4(ctx, operation, x, y); else if (type == 5)
- draw5(ctx, operation, x, y); else if (type == 6)
- draw6(ctx, operation, x, y);
- }
- function draw6(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x + l / 2, y + l)
- ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x + l / 2, y, r, 2 * PI, 1 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function draw5(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l / 2, y)
- ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x + l / 2, y + l, r, 1 * PI, 2 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function draw4(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l / 2, y)
- ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x + l / 2, y + l)
- ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function draw3(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l / 2, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x + l / 2, y + l)
- ctx.arc(x + l / 2, y + l + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function draw2(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l / 2, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function draw1(ctx, operation, x, y) {
- ctx.beginPath()
- ctx.moveTo(x, y)
- ctx.lineTo(x + l / 2, y)
- ctx.arc(x + l / 2, y - r + 4, r, 0, 2 * PI)
- ctx.lineTo(x + l / 2, y)
- ctx.lineTo(x + l, y)
- ctx.lineTo(x + l, y + l / 2)
- ctx.arc(x + l + r - 4, y + l / 2, r, 0, 2 * PI)
- ctx.lineTo(x + l, y + l / 2)
- ctx.lineTo(x + l, y + l)
- ctx.lineTo(x, y + l)
- ctx.lineTo(x, y)
- ctx.fillStyle = '#fff'
- ctx[operation]()
- ctx.beginPath()
- ctx.arc(x, y + l / 2, r, 1.5 * PI, 0.5 * PI)
- ctx.globalCompositeOperation = "xor"
- ctx.fill()
- }
- function sum(x, y) {
- return x + y
- }
- function square(x) {
- return x * x
- }
- class sliderImgPuzzle {
- constructor({el, onSuccess, onFail, onRefresh}) {
- this.el = el
- this.onSuccess = onSuccess
- this.onFail = onFail
- this.onRefresh = onRefresh
- this.isEnable = true
- }
- init() {
- this.initDOM()
- this.initImg()
- this.draw()
- this.bindEvents()
- return this;
- }
- initDOM() {
- const canvas = createCanvas(w, h)
- const block = canvas.cloneNode(true)
- const canvasContainer = createElement('div')
- const sliderImgPuzzleContainer = createElement('div')
- const refreshIcon = createElement('div')
- const sliderImgPuzzleMask = createElement('div')
- const sliderImgPuzzle = createElement('div')
- const sliderImgPuzzleIcon = createElement('span')
- const text = createElement('span')
- canvasContainer.className = 'canvasContainer'
- block.className = 'block'
- sliderImgPuzzleContainer.className = 'sliderImgPuzzleContainer'
- refreshIcon.className = 'refreshIcon'
- sliderImgPuzzleMask.className = 'sliderImgPuzzleMask'
- sliderImgPuzzle.className = 'sliderImgPuzzle'
- sliderImgPuzzleIcon.className = 'sliderImgPuzzleIcon'
- text.innerHTML = '向右滑动滑块填充拼图'
- text.className = 'sliderImgPuzzleText'
- const el = this.el
- canvasContainer.appendChild(canvas)
- canvasContainer.appendChild(refreshIcon)
- canvasContainer.appendChild(block)
- el.appendChild(canvasContainer)
- sliderImgPuzzle.appendChild(sliderImgPuzzleIcon)
- sliderImgPuzzleMask.appendChild(sliderImgPuzzle)
- sliderImgPuzzleContainer.appendChild(sliderImgPuzzleMask)
- sliderImgPuzzleContainer.appendChild(text)
- el.appendChild(sliderImgPuzzleContainer)
- Object.assign(this, {
- canvas,
- block,
- canvasContainer,
- sliderImgPuzzleContainer,
- refreshIcon,
- sliderImgPuzzle,
- sliderImgPuzzleMask,
- sliderImgPuzzleIcon,
- text,
- canvasCtx: canvas.getContext('2d'),
- blockCtx: block.getContext('2d')
- })
- }
- initImg() {
- const img = createImg(() => {
- this.canvasCtx.drawImage(img, 0, 0, w, h)
- this.blockCtx.drawImage(img, 0, 0, w, h)
- const y = this.y - r * 2 + 2
- const ImageData = this.blockCtx.getImageData(this.x, y, L + r + 4, L + r + 4)
- this.block.width = L
- this.blockCtx.putImageData(ImageData, 0, y)
- })
- this.img = img
- }
- draw() {
- this.x = getRandomNumberByRange(L + 10, w - (L + 10))
- this.y = getRandomNumberByRange(10 + r * 2, h - (L + 10))
- var num = Math.floor(Math.random() * 6 + 1);
- draw(this.canvasCtx, 'fill', this.x, this.y, num)
- draw(this.blockCtx, 'clip', this.x, this.y, num)
- }
- clean() {
- this.canvasCtx.clearRect(0, 0, w, h)
- this.blockCtx.clearRect(0, 0, w, h)
- this.block.width = w
- }
- bindEvents() {
- this.el.onselectstart = () => false
- this.refreshIcon.onclick = () => {
- this.reset()
- typeof this.onRefresh === 'function' && this.onRefresh()
- }
- let originX, originY, trail = [], isMouseDown = false
- this.sliderImgPuzzle.ctrl = this;
- this.sliderImgPuzzle.addEventListener('mousedown', function (e) {
- if (!this.parentElement.parentElement.classList.contains("sliderImgPuzzleContainer_success")) {
- originX = e.x, originY = e.y
- isMouseDown = true
- }
- else {
- isMouseDown = false
- }
- if (!this.ctrl.isEnable) isMouseDown = false;
- })
- // this.sliderImgPuzzle.addEventListener('mouseover', function (e) {
- // if (this.ctrl.isEnable)
- // this.parentElement.parentElement.parentElement.querySelector(".canvasContainer").style.display = "block";
- // })
- // this.canvasContainer.parentElement.addEventListener('mouseleave', function (e) {
- // this.querySelector(".canvasContainer").style.display = "none";
- // })
- document.addEventListener('mousemove', (e) => {
- if (!isMouseDown) return false
- const moveX = e.x - originX
- const moveY = e.y - originY
- if (moveX < 0 || moveX + 38 >= w) return false
- this.sliderImgPuzzle.style.left = moveX + 'px'
- var blockLeft = (w - 40 - 20) / (w - 40) * moveX
- this.block.style.left = blockLeft + 'px'
- addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_active')
- this.sliderImgPuzzleMask.style.width = moveX + 'px'
- trail.push(moveY)
- })
- document.addEventListener('mouseup', (e) => {
- if (!isMouseDown) return false
- isMouseDown = false
- if (e.x == originX) return false
- removeClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_active')
- this.trail = trail
- const {spliced, TuringTest} = this.verify()
- if (spliced) {
- if (TuringTest) {
- addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_success')
- typeof this.onSuccess === 'function' && this.onSuccess()
- } else {
- addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_fail')
- this.text.innerHTML = '再试一次'
- this.reset()
- }
- } else {
- addClass(this.sliderImgPuzzleContainer, 'sliderImgPuzzleContainer_fail')
- typeof this.onFail === 'function' && this.onFail()
- setTimeout(() => {
- this.reset()
- }, 1000)
- }
- })
- }
- verify() {
- const arr = this.trail
- const average = arr.reduce(sum) / arr.length
- const deviations = arr.map(x => x - average)
- const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length)
- const left = parseInt(this.block.style.left)
- return {spliced: Math.abs(left - this.x) < 10, TuringTest: average !== stddev,}
- }
- reset() {
- this.sliderImgPuzzleContainer.className = 'sliderImgPuzzleContainer'
- this.sliderImgPuzzle.style.left = 0
- this.block.style.left = 0
- this.sliderImgPuzzleMask.style.width = 0
- this.clean()
- this.img.src = getRandomImg()
- this.draw()
- }
- enablePuzzle(enable) {
- if (enable) {
- this.isEnable = true;
- }
- else this.isEnable = false;
- }
- }
- this.each(function () {
- var $SliderImgPuzzle = $(this);
- setting.el = $SliderImgPuzzle[0];
- var sip = new sliderImgPuzzle(setting).init();
- $SliderImgPuzzle.data("LsSliderImgPuzzle", sip);
- });
- }
- })(jQuery);
组件HTML(sliderImgPuzzle.html)
- <!DOCTYPE html>
- <html lang="zh">
- <head>
- <meta charset="UTF-8">
- <title>滑动拼图验证码</title>
- <link rel="stylesheet" href="../css/sliderImgPuzzle.css">
- <style>
- .container {
- width: 310px;
- margin: 300px auto;
- }
- input {
- display: block;
- width: 290px;
- line-height: 40px;
- margin: 10px 0;
- padding: 0 10px;
- outline: none;
- border:1px solid #c8cccf;
- border-radius: 4px;
- color:#6a6f77;
- }
- #msg {
- width: 100%;
- line-height: 40px;
- font-size: 14px;
- text-align: center;
- }
- a:link,a:visited,a:hover,a:active {
- margin-left: 100px;
- color: #0366D6;
- }
- </style>
- </head>
- <body>
- <div class="container">
- <div id="silderpuzzle" style="position: relative;padding-bottom: 15px;"></div>
- <div id="msg"></div>
- <button id="setenable">请先设置控件为可用</button>
- <button id="setdisabled">设置控件不可用</button>
- <button id="reflash">刷新控件</button>
- </div>
- <script src="http://www.jq22.com/jquery/jquery-2.1.1.js"></script>
- <script src="../js/sliderImgPuzzle.js"></script>
- <script>
- //--------------------滑块拼图验证控件初始化-----
- var option = {
- onSuccess: function () {
- document.getElementById('msg').innerHTML = '验证成功!'
- },
- onFail: cleanMsg,
- onRefresh: cleanMsg
- }
- $("#silderpuzzle").SliderImgPuzzle(option);
- //--------------------滑块拼图验证控件初始化完成-----
- //--------------------获取控件对象-----
- var imgPuzzle = $("#silderpuzzle").data("LsSliderImgPuzzle");
- //--------------------回调函数
- function cleanMsg() {
- alert("回调函数")
- }
- $("#setenable").on("click",function(){
- //--------------------对象提供的enable的方法
- imgPuzzle.enablePuzzle(true);
- })
- $("#setdisabled").on("click",function(){
- //--------------------对象提供的enable的方法
- imgPuzzle.enablePuzzle(false);
- })
- $("#reflash").on("click",function(){
- //--------------------对象提供的enable的方法
- imgPuzzle.reset();
- })
- </script>
- </body>
- </html>
总结:
该组件为轻量级组件,使用简单,一看便知。支持自定义扩展,如有不会的朋友可以拿来即用,非常方便!
GitHub地址:
https://github.com/TopSkyhua/SliderImgPuzzle
Web前端图形滑块检验组件实现的更多相关文章
- 二十三、【开源】EFW框架Web前端开发之常用组件(FusionCharts图表、ReportAll报表等)
回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.2:http://pan.baidu.com/s/1hcnuA EFW框架实例源代码下载:http://pan ...
- 前端组件库 - 搭建web app常用的样式/组件等收集列表(移动优先)
0. 前端自动化(Workflow) 前端构建工具 Webpack - module bundler Yeoman - a set of tools for automating developmen ...
- web 前端常用组件【05】ZTree
web 项目或多或少都会有涉及到什么人员职称树,菜单树,组织机构树等. 历手三四个项目有大有小,采用的树前端都是 Ztree. 有些优秀的J2EE 框架将这些常用的组件都封装起来,作为模块化的组件提供 ...
- Web前端性能优化教程04:压缩组件
本文是Web前端性能优化系列文章中的第四篇,主要讲述内容:压缩组件.完整教程可查看:Web前端性能优化 基础知识 gzip编码:gzip是GUNzip的缩写,是使用无损压缩算法的一种,最早是用于Uni ...
- web前端开发常用组件
web前端开发常用组件 1. 对话框(dialog):jbox(适合对话框等其它功能).colorbox(也很强大,可以弥补jbox图片轮播的落点), 这二者基本能搞定所有对话框的情况 2. ...
- Web 前端 UI 组件库文档自动化方案 All In One
Web 前端 UI 组件库文档自动化方案 All In One 需求 自动化 动态 好用 markdown element-ui 中示例和说明按照一定规则写在md文件中,调用md-loader将md文 ...
- 整理六百篇web前端知识混总
9个有用的和免费的工具来支持动态网页开发 8个基本的引导工具的网页设计师 11款CSS3动画工具的开发 2016年某前端群题目答案参考 9最好的JavaScript压缩工具 创建响应式布局的10款优秀 ...
- Web 前端开发精华文章推荐(jQuery、HTML5、CSS3)【系列十二】
2012年12月12日,[<Web 前端开发人员和设计师必读文章>系列十二]和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HT ...
- HTML5零基础学习Web前端需要知道哪些?
HTML零基础学习Web前端网页制作,首先是要掌握一些常用标签的使用和他们的各个属性,常用的标签我总结了一下有以下这些: html:页面的根元素. head:页面的头部标签,是所有头部元素的容器. b ...
随机推荐
- 域名可以解析(ping域名可以获取正确ip),服务器本地telnet 域名+端口 无法连接,通过建立本地虚拟域名指定的方法解决该问题
环境: 服务器A,网管已为A开通外网ip,且设置有映射域名:假如内网ip为172.16.2.6.外网ip为123.123.123.123.域名为test.sstest.com 现象: 服务器A,tel ...
- jquery中通过trim() length 判断数据是否有值
在jquery中可以通过如下方式判断某一个字符串是否有值,结合if else if 语句进行业务逻辑的处理 <!DOCTYPE html> <html lang="en&q ...
- git简易入门(github)
本文讲解下git的使用,包括使用git上传项目工程到github,以及错误解决. 1.安装git 使用apt-get安 sudo apt-get update sudo apt-get install ...
- python基础知识梳理-----7函数
基本内容梳理 1:函数定义,函数名,函数体以及函数的调用方式 2:函数的返回值 3:函数的参数 4:函数---动态传参数 5:名称空间,局部名称的加载顺序,全局名称空间,作用域,加载顺序 6:函数的嵌 ...
- JDK源码分析(10) ConcurrentLinkedQueue
概述 我们要实现一个线程安全的队列有两种实现方法一种是使用阻塞算法,另一种是使用非阻塞算法.使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现,而非阻塞 ...
- Redis快照持久化
Redis的持久化功能: redis为了内部数据的安全考虑,会把本身的数据以文件的形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)里边. 数据保存到硬盘的过程就被称为” ...
- Android MediaPlayer 常用方法介绍
Android MediaPlayer 常用方法介绍 方法:create(Context context, Uri uri) 解释:静态方法,通过Uri创建一个多媒体播放器. 方法:create(Co ...
- 查看和修改mysql数据库的最大链接数据
通常,mysql的最大连接数默认是100, 最大可以达到16384.1.查看最大连接数:show variables like '%max_connections%';2.修改最大连接数方法一:修改配 ...
- 关于function构造函数特别注意的
function在javascript中是对象,所以function持有构造函数例子:var a = new Function("x","y","re ...
- log4php0.9的详细配备实例说明
一.什么是log4php: log4j在JAVA中可算是大名鼎鼎的日志开发包了,它为apche组织维护项目,VxR兄使用php来实现了log4j的功能, 目前log4php已经作为log4j的一个子项 ...